[Zrouter-src-freebsd] ZRouter.org: push to FreeBSD HEAD tree

zrouter-src-freebsd at zrouter.org zrouter-src-freebsd at zrouter.org
Fri Mar 2 15:37:46 UTC 2012


details:   http://zrouter.org/hg/FreeBSD/head//rev/a6a608a13e37
changeset: 355:a6a608a13e37
user:      ray at terran.dlink.ua
date:      Fri Mar 02 16:54:40 2012 +0200
description:
Update to FreeBSD-HEAD @232391

diffstat:

 head/contrib/gcc/gcc.c                                                         |    13 +-
 head/contrib/libarchive/COPYING                                                |     4 +-
 head/contrib/libarchive/FREEBSD-Xlist                                          |     4 +-
 head/contrib/libarchive/NEWS                                                   |    93 +-
 head/contrib/libarchive/README                                                 |    35 +-
 head/contrib/libarchive/cpio/bsdcpio.1                                         |    67 +-
 head/contrib/libarchive/cpio/cmdline.c                                         |    23 +-
 head/contrib/libarchive/cpio/cpio.c                                            |   164 +-
 head/contrib/libarchive/cpio/cpio.h                                            |    10 +-
 head/contrib/libarchive/cpio/test/main.c                                       |   781 +-
 head/contrib/libarchive/cpio/test/test.h                                       |   100 +-
 head/contrib/libarchive/cpio/test/test_0.c                                     |    16 +-
 head/contrib/libarchive/cpio/test/test_basic.c                                 |    51 +-
 head/contrib/libarchive/cpio/test/test_format_newc.c                           |    47 +-
 head/contrib/libarchive/cpio/test/test_option_c.c                              |    22 +-
 head/contrib/libarchive/cpio/test/test_option_t.c                              |    33 +-
 head/contrib/libarchive/cpio/test/test_option_u.c                              |     8 +-
 head/contrib/libarchive/cpio/test/test_owner_parse.c                           |    13 +-
 head/contrib/libarchive/libarchive/archive.h                                   |   513 +-
 head/contrib/libarchive/libarchive/archive_check_magic.c                       |    82 +-
 head/contrib/libarchive/libarchive/archive_crc32.h                             |    14 +-
 head/contrib/libarchive/libarchive/archive_entry.3                             |   339 +-
 head/contrib/libarchive/libarchive/archive_entry.c                             |  1339 +-
 head/contrib/libarchive/libarchive/archive_entry.h                             |   240 +-
 head/contrib/libarchive/libarchive/archive_entry_copy_stat.c                   |    10 +-
 head/contrib/libarchive/libarchive/archive_entry_link_resolver.c               |   157 +-
 head/contrib/libarchive/libarchive/archive_entry_private.h                     |    88 +-
 head/contrib/libarchive/libarchive/archive_entry_stat.c                        |     6 +-
 head/contrib/libarchive/libarchive/archive_private.h                           |    50 +-
 head/contrib/libarchive/libarchive/archive_read.3                              |   702 +-
 head/contrib/libarchive/libarchive/archive_read.c                              |   925 +-
 head/contrib/libarchive/libarchive/archive_read_data_into_fd.c                 |    84 +-
 head/contrib/libarchive/libarchive/archive_read_disk.3                         |    33 +-
 head/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c         |   567 +-
 head/contrib/libarchive/libarchive/archive_read_disk_private.h                 |    15 +-
 head/contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c     |    30 +-
 head/contrib/libarchive/libarchive/archive_read_extract.c                      |    13 +-
 head/contrib/libarchive/libarchive/archive_read_open_fd.c                      |    89 +-
 head/contrib/libarchive/libarchive/archive_read_open_file.c                    |    46 +-
 head/contrib/libarchive/libarchive/archive_read_open_filename.c                |   431 +-
 head/contrib/libarchive/libarchive/archive_read_open_memory.c                  |    79 +-
 head/contrib/libarchive/libarchive/archive_read_private.h                      |    57 +-
 head/contrib/libarchive/libarchive/archive_read_support_format_all.c           |    40 +-
 head/contrib/libarchive/libarchive/archive_read_support_format_ar.c            |   145 +-
 head/contrib/libarchive/libarchive/archive_read_support_format_cpio.c          |   592 +-
 head/contrib/libarchive/libarchive/archive_read_support_format_empty.c         |    22 +-
 head/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c       |   454 +-
 head/contrib/libarchive/libarchive/archive_read_support_format_mtree.c         |   634 +-
 head/contrib/libarchive/libarchive/archive_read_support_format_raw.c           |    60 +-
 head/contrib/libarchive/libarchive/archive_read_support_format_tar.c           |  1182 +-
 head/contrib/libarchive/libarchive/archive_read_support_format_xar.c           |   424 +-
 head/contrib/libarchive/libarchive/archive_read_support_format_zip.c           |  1269 +-
 head/contrib/libarchive/libarchive/archive_string.c                            |  4326 ++++-
 head/contrib/libarchive/libarchive/archive_string.h                            |   180 +-
 head/contrib/libarchive/libarchive/archive_string_sprintf.c                    |    52 +-
 head/contrib/libarchive/libarchive/archive_util.3                              |    76 +-
 head/contrib/libarchive/libarchive/archive_util.c                              |   499 +-
 head/contrib/libarchive/libarchive/archive_virtual.c                           |    61 +-
 head/contrib/libarchive/libarchive/archive_write.3                             |   550 +-
 head/contrib/libarchive/libarchive/archive_write.c                             |   583 +-
 head/contrib/libarchive/libarchive/archive_write_disk.3                        |    48 +-
 head/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c    |    14 +-
 head/contrib/libarchive/libarchive/archive_write_open_filename.c               |   108 +-
 head/contrib/libarchive/libarchive/archive_write_open_memory.c                 |    14 +-
 head/contrib/libarchive/libarchive/archive_write_private.h                     |    66 +-
 head/contrib/libarchive/libarchive/archive_write_set_format.c                  |     8 +-
 head/contrib/libarchive/libarchive/archive_write_set_format_ar.c               |    60 +-
 head/contrib/libarchive/libarchive/archive_write_set_format_by_name.c          |    12 +-
 head/contrib/libarchive/libarchive/archive_write_set_format_cpio.c             |   319 +-
 head/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c        |   340 +-
 head/contrib/libarchive/libarchive/archive_write_set_format_mtree.c            |  1131 +-
 head/contrib/libarchive/libarchive/archive_write_set_format_pax.c              |  1193 +-
 head/contrib/libarchive/libarchive/archive_write_set_format_shar.c             |    93 +-
 head/contrib/libarchive/libarchive/archive_write_set_format_ustar.c            |   351 +-
 head/contrib/libarchive/libarchive/archive_write_set_format_zip.c              |   338 +-
 head/contrib/libarchive/libarchive/libarchive-formats.5                        |     8 +-
 head/contrib/libarchive/libarchive/libarchive.3                                |   141 +-
 head/contrib/libarchive/libarchive/libarchive_internals.3                      |     8 +-
 head/contrib/libarchive/libarchive/tar.5                                       |   200 +-
 head/contrib/libarchive/libarchive/test/main.c                                 |   773 +-
 head/contrib/libarchive/libarchive/test/read_open_memory.c                     |   100 +-
 head/contrib/libarchive/libarchive/test/test.h                                 |    98 +-
 head/contrib/libarchive/libarchive/test/test_acl_freebsd.c                     |     6 +-
 head/contrib/libarchive/libarchive/test/test_acl_pax.c                         |   267 +-
 head/contrib/libarchive/libarchive/test/test_archive_api_feature.c             |    30 +-
 head/contrib/libarchive/libarchive/test/test_bad_fd.c                          |    12 +-
 head/contrib/libarchive/libarchive/test/test_compat_bzip2.c                    |    12 +-
 head/contrib/libarchive/libarchive/test/test_compat_cpio.c                     |     6 +-
 head/contrib/libarchive/libarchive/test/test_compat_gtar.c                     |    14 +-
 head/contrib/libarchive/libarchive/test/test_compat_gzip.c                     |    10 +-
 head/contrib/libarchive/libarchive/test/test_compat_lzma.c                     |    10 +-
 head/contrib/libarchive/libarchive/test/test_compat_solaris_tar_acl.c          |     6 +-
 head/contrib/libarchive/libarchive/test/test_compat_tar_hardlink.c             |    10 +-
 head/contrib/libarchive/libarchive/test/test_compat_xz.c                       |    10 +-
 head/contrib/libarchive/libarchive/test/test_compat_zip.c                      |   369 +-
 head/contrib/libarchive/libarchive/test/test_compat_zip_2.zip.uu               |     2 -
 head/contrib/libarchive/libarchive/test/test_empty_write.c                     |    14 +-
 head/contrib/libarchive/libarchive/test/test_entry.c                           |   159 +-
 head/contrib/libarchive/libarchive/test/test_extattr_freebsd.c                 |     7 +-
 head/contrib/libarchive/libarchive/test/test_fuzz.c                            |    69 +-
 head/contrib/libarchive/libarchive/test/test_open_failure.c                    |    42 +-
 head/contrib/libarchive/libarchive/test/test_open_fd.c                         |    12 +-
 head/contrib/libarchive/libarchive/test/test_open_file.c                       |     8 +-
 head/contrib/libarchive/libarchive/test/test_open_filename.c                   |   105 +-
 head/contrib/libarchive/libarchive/test/test_pax_filename_encoding.c           |   298 +-
 head/contrib/libarchive/libarchive/test/test_read_compress_program.c           |    14 +-
 head/contrib/libarchive/libarchive/test/test_read_data_large.c                 |    34 +-
 head/contrib/libarchive/libarchive/test/test_read_disk.c                       |     8 +-
 head/contrib/libarchive/libarchive/test/test_read_disk_entry_from_file.c       |    10 +-
 head/contrib/libarchive/libarchive/test/test_read_extract.c                    |    12 +-
 head/contrib/libarchive/libarchive/test/test_read_file_nonexistent.c           |     4 +-
 head/contrib/libarchive/libarchive/test/test_read_format_ar.c                  |    15 +-
 head/contrib/libarchive/libarchive/test/test_read_format_cpio_bin.c            |    23 +-
 head/contrib/libarchive/libarchive/test/test_read_format_cpio_bin_Z.c          |    11 +-
 head/contrib/libarchive/libarchive/test/test_read_format_cpio_bin_be.c         |     6 +-
 head/contrib/libarchive/libarchive/test/test_read_format_cpio_bin_bz2.c        |     8 +-
 head/contrib/libarchive/libarchive/test/test_read_format_cpio_bin_gz.c         |    12 +-
 head/contrib/libarchive/libarchive/test/test_read_format_cpio_bin_lzma.c       |    10 +-
 head/contrib/libarchive/libarchive/test/test_read_format_cpio_bin_xz.c         |    10 +-
 head/contrib/libarchive/libarchive/test/test_read_format_cpio_odc.c            |    12 +-
 head/contrib/libarchive/libarchive/test/test_read_format_cpio_svr4_bzip2_rpm.c |     8 +-
 head/contrib/libarchive/libarchive/test/test_read_format_cpio_svr4_gzip.c      |    10 +-
 head/contrib/libarchive/libarchive/test/test_read_format_cpio_svr4_gzip_rpm.c  |     8 +-
 head/contrib/libarchive/libarchive/test/test_read_format_cpio_svr4c_Z.c        |    10 +-
 head/contrib/libarchive/libarchive/test/test_read_format_empty.c               |    12 +-
 head/contrib/libarchive/libarchive/test/test_read_format_gtar_gz.c             |    11 +-
 head/contrib/libarchive/libarchive/test/test_read_format_gtar_lzma.c           |    14 +-
 head/contrib/libarchive/libarchive/test/test_read_format_gtar_sparse.c         |    28 +-
 head/contrib/libarchive/libarchive/test/test_read_format_iso_Z.c               |    18 +-
 head/contrib/libarchive/libarchive/test/test_read_format_iso_multi_extent.c    |    10 +-
 head/contrib/libarchive/libarchive/test/test_read_format_isojoliet_bz2.c       |    14 +-
 head/contrib/libarchive/libarchive/test/test_read_format_isojoliet_long.c      |    12 +-
 head/contrib/libarchive/libarchive/test/test_read_format_isojoliet_rr.c        |    12 +-
 head/contrib/libarchive/libarchive/test/test_read_format_isorr_bz2.c           |    10 +-
 head/contrib/libarchive/libarchive/test/test_read_format_isorr_ce.c            |    10 +-
 head/contrib/libarchive/libarchive/test/test_read_format_isorr_new_bz2.c       |    10 +-
 head/contrib/libarchive/libarchive/test/test_read_format_isorr_rr_moved.c      |    10 +-
 head/contrib/libarchive/libarchive/test/test_read_format_isozisofs_bz2.c       |    10 +-
 head/contrib/libarchive/libarchive/test/test_read_format_mtree.c               |   263 +-
 head/contrib/libarchive/libarchive/test/test_read_format_mtree.mtree.uu        |    12 +-
 head/contrib/libarchive/libarchive/test/test_read_format_pax_bz2.c             |     7 +-
 head/contrib/libarchive/libarchive/test/test_read_format_raw.c                 |    14 +-
 head/contrib/libarchive/libarchive/test/test_read_format_tar.c                 |    22 +-
 head/contrib/libarchive/libarchive/test/test_read_format_tar_empty_filename.c  |    10 +-
 head/contrib/libarchive/libarchive/test/test_read_format_tbz.c                 |     9 +-
 head/contrib/libarchive/libarchive/test/test_read_format_tgz.c                 |    11 +-
 head/contrib/libarchive/libarchive/test/test_read_format_tlz.c                 |    11 +-
 head/contrib/libarchive/libarchive/test/test_read_format_txz.c                 |    11 +-
 head/contrib/libarchive/libarchive/test/test_read_format_tz.c                  |    11 +-
 head/contrib/libarchive/libarchive/test/test_read_format_xar.c                 |    21 +-
 head/contrib/libarchive/libarchive/test/test_read_format_zip.c                 |   321 +-
 head/contrib/libarchive/libarchive/test/test_read_format_zip.zip.uu            |     4 +-
 head/contrib/libarchive/libarchive/test/test_read_large.c                      |    19 +-
 head/contrib/libarchive/libarchive/test/test_read_pax_truncated.c              |   168 +-
 head/contrib/libarchive/libarchive/test/test_read_position.c                   |    86 +-
 head/contrib/libarchive/libarchive/test/test_read_truncated.c                  |    74 +-
 head/contrib/libarchive/libarchive/test/test_read_uu.c                         |    73 +-
 head/contrib/libarchive/libarchive/test/test_tar_filenames.c                   |    32 +-
 head/contrib/libarchive/libarchive/test/test_tar_large.c                       |    39 +-
 head/contrib/libarchive/libarchive/test/test_ustar_filenames.c                 |    20 +-
 head/contrib/libarchive/libarchive/test/test_write_compress.c                  |    39 +-
 head/contrib/libarchive/libarchive/test/test_write_compress_bzip2.c            |    94 +-
 head/contrib/libarchive/libarchive/test/test_write_compress_gzip.c             |    92 +-
 head/contrib/libarchive/libarchive/test/test_write_compress_lzma.c             |    87 +-
 head/contrib/libarchive/libarchive/test/test_write_compress_program.c          |    31 +-
 head/contrib/libarchive/libarchive/test/test_write_compress_xz.c               |    86 +-
 head/contrib/libarchive/libarchive/test/test_write_disk.c                      |   109 +-
 head/contrib/libarchive/libarchive/test/test_write_disk_failures.c             |    15 +-
 head/contrib/libarchive/libarchive/test/test_write_disk_hardlink.c             |    14 +-
 head/contrib/libarchive/libarchive/test/test_write_disk_perms.c                |    12 +-
 head/contrib/libarchive/libarchive/test/test_write_disk_secure.c               |    10 +-
 head/contrib/libarchive/libarchive/test/test_write_disk_sparse.c               |     6 +-
 head/contrib/libarchive/libarchive/test/test_write_disk_symlink.c              |     4 +-
 head/contrib/libarchive/libarchive/test/test_write_disk_times.c                |     4 +-
 head/contrib/libarchive/libarchive/test/test_write_format_ar.c                 |    53 +-
 head/contrib/libarchive/libarchive/test/test_write_format_cpio.c               |   124 +-
 head/contrib/libarchive/libarchive/test/test_write_format_cpio_empty.c         |    10 +-
 head/contrib/libarchive/libarchive/test/test_write_format_cpio_newc.c          |     9 +-
 head/contrib/libarchive/libarchive/test/test_write_format_cpio_odc.c           |     8 +-
 head/contrib/libarchive/libarchive/test/test_write_format_mtree.c              |    34 +-
 head/contrib/libarchive/libarchive/test/test_write_format_pax.c                |    61 +-
 head/contrib/libarchive/libarchive/test/test_write_format_shar_empty.c         |    10 +-
 head/contrib/libarchive/libarchive/test/test_write_format_tar.c                |    89 +-
 head/contrib/libarchive/libarchive/test/test_write_format_tar_empty.c          |    28 +-
 head/contrib/libarchive/libarchive/test/test_write_format_tar_ustar.c          |    39 +-
 head/contrib/libarchive/libarchive/test/test_write_format_zip.c                |   191 +-
 head/contrib/libarchive/libarchive/test/test_write_format_zip_empty.c          |    38 +-
 head/contrib/libarchive/libarchive/test/test_write_format_zip_no_compression.c |    50 +-
 head/contrib/libarchive/libarchive/test/test_write_open_memory.c               |    44 +-
 head/contrib/libarchive/libarchive_fe/line_reader.c                            |    62 +-
 head/contrib/libarchive/libarchive_fe/matching.c                               |    59 +-
 head/contrib/libarchive/libarchive_fe/pathmatch.c                              |     4 +-
 head/contrib/libarchive/tar/bsdtar.1                                           |    53 +-
 head/contrib/libarchive/tar/bsdtar.c                                           |   136 +-
 head/contrib/libarchive/tar/bsdtar.h                                           |    19 +-
 head/contrib/libarchive/tar/bsdtar_platform.h                                  |     6 +-
 head/contrib/libarchive/tar/cmdline.c                                          |   100 +-
 head/contrib/libarchive/tar/getdate.c                                          |     4 +-
 head/contrib/libarchive/tar/read.c                                             |    81 +-
 head/contrib/libarchive/tar/subst.c                                            |    55 +-
 head/contrib/libarchive/tar/test/main.c                                        |   685 +-
 head/contrib/libarchive/tar/test/test.h                                        |   101 +-
 head/contrib/libarchive/tar/test/test_0.c                                      |    16 +-
 head/contrib/libarchive/tar/test/test_basic.c                                  |   116 +-
 head/contrib/libarchive/tar/test/test_option_T_upper.c                         |    64 +-
 head/contrib/libarchive/tar/test/test_option_q.c                               |    28 +-
 head/contrib/libarchive/tar/test/test_option_r.c                               |    97 +-
 head/contrib/libarchive/tar/test/test_option_s.c                               |   214 +-
 head/contrib/libarchive/tar/test/test_patterns.c                               |    12 +-
 head/contrib/libarchive/tar/test/test_strip_components.c                       |    82 +-
 head/contrib/libarchive/tar/test/test_symlink_dir.c                            |    36 +-
 head/contrib/libarchive/tar/tree.c                                             |    71 +-
 head/contrib/libarchive/tar/util.c                                             |    48 +-
 head/contrib/libarchive/tar/write.c                                            |   207 +-
 head/contrib/netcat/netcat.c                                                   |    15 +-
 head/contrib/pf/pfctl/parse.y                                                  |    34 +-
 head/contrib/tcsh/Fixes                                                        |   144 +
 head/contrib/tcsh/Imakefile                                                    |     8 +-
 head/contrib/tcsh/Makefile.in                                                  |   313 +-
 head/contrib/tcsh/Ported                                                       |    23 +-
 head/contrib/tcsh/README                                                       |     4 +-
 head/contrib/tcsh/WishList                                                     |    11 -
 head/contrib/tcsh/complete.tcsh                                                |     4 +-
 head/contrib/tcsh/config.guess                                                 |   149 +-
 head/contrib/tcsh/config.h.in                                                  |    59 +-
 head/contrib/tcsh/config.sub                                                   |    39 +-
 head/contrib/tcsh/config/bsd4.4                                                |     5 -
 head/contrib/tcsh/config_f.h                                                   |    24 +-
 head/contrib/tcsh/configure                                                    |  9627 ++++-----
 head/contrib/tcsh/configure.in                                                 |    61 +-
 head/contrib/tcsh/ed.chared.c                                                  |    23 +-
 head/contrib/tcsh/ed.inputl.c                                                  |     6 +-
 head/contrib/tcsh/ed.refresh.c                                                 |   110 +-
 head/contrib/tcsh/ed.screen.c                                                  |    64 +-
 head/contrib/tcsh/ed.term.c                                                    |     7 +-
 head/contrib/tcsh/gethost.c                                                    |    76 +-
 head/contrib/tcsh/glob.c                                                       |   141 +-
 head/contrib/tcsh/glob.h                                                       |     2 +
 head/contrib/tcsh/host.defs                                                    |   583 +-
 head/contrib/tcsh/install-sh                                                   |   513 +-
 head/contrib/tcsh/nls/C/charset                                                |     4 +-
 head/contrib/tcsh/nls/C/set19                                                  |    12 +-
 head/contrib/tcsh/nls/et/charset                                               |     4 +-
 head/contrib/tcsh/nls/et/set1                                                  |   140 +-
 head/contrib/tcsh/nls/et/set10                                                 |     8 +-
 head/contrib/tcsh/nls/et/set11                                                 |     6 +-
 head/contrib/tcsh/nls/et/set13                                                 |    10 +-
 head/contrib/tcsh/nls/et/set14                                                 |     4 +-
 head/contrib/tcsh/nls/et/set15                                                 |     6 +-
 head/contrib/tcsh/nls/et/set16                                                 |     6 +-
 head/contrib/tcsh/nls/et/set17                                                 |    12 +-
 head/contrib/tcsh/nls/et/set18                                                 |     4 +-
 head/contrib/tcsh/nls/et/set19                                                 |    26 +-
 head/contrib/tcsh/nls/et/set2                                                  |    58 +-
 head/contrib/tcsh/nls/et/set20                                                 |    46 +-
 head/contrib/tcsh/nls/et/set21                                                 |     6 +-
 head/contrib/tcsh/nls/et/set22                                                 |    14 +-
 head/contrib/tcsh/nls/et/set23                                                 |    12 +-
 head/contrib/tcsh/nls/et/set24                                                 |     4 +-
 head/contrib/tcsh/nls/et/set25                                                 |     4 +-
 head/contrib/tcsh/nls/et/set26                                                 |    10 +-
 head/contrib/tcsh/nls/et/set27                                                 |     6 +-
 head/contrib/tcsh/nls/et/set3                                                  |   186 +-
 head/contrib/tcsh/nls/et/set30                                                 |     4 +-
 head/contrib/tcsh/nls/et/set4                                                  |    46 +-
 head/contrib/tcsh/nls/et/set5                                                  |     4 +-
 head/contrib/tcsh/nls/et/set6                                                  |     8 +-
 head/contrib/tcsh/nls/et/set7                                                  |    24 +-
 head/contrib/tcsh/nls/et/set8                                                  |     4 +-
 head/contrib/tcsh/nls/et/set9                                                  |    10 +-
 head/contrib/tcsh/nls/finnish/charset                                          |     4 +-
 head/contrib/tcsh/nls/finnish/set1                                             |   134 +-
 head/contrib/tcsh/nls/finnish/set10                                            |    10 +-
 head/contrib/tcsh/nls/finnish/set11                                            |    10 +-
 head/contrib/tcsh/nls/finnish/set12                                            |     4 +-
 head/contrib/tcsh/nls/finnish/set13                                            |    12 +-
 head/contrib/tcsh/nls/finnish/set14                                            |     4 +-
 head/contrib/tcsh/nls/finnish/set16                                            |     6 +-
 head/contrib/tcsh/nls/finnish/set17                                            |     8 +-
 head/contrib/tcsh/nls/finnish/set18                                            |     4 +-
 head/contrib/tcsh/nls/finnish/set19                                            |    20 +-
 head/contrib/tcsh/nls/finnish/set2                                             |    90 +-
 head/contrib/tcsh/nls/finnish/set20                                            |    22 +-
 head/contrib/tcsh/nls/finnish/set22                                            |    10 +-
 head/contrib/tcsh/nls/finnish/set23                                            |    24 +-
 head/contrib/tcsh/nls/finnish/set25                                            |     4 +-
 head/contrib/tcsh/nls/finnish/set26                                            |    10 +-
 head/contrib/tcsh/nls/finnish/set27                                            |     4 +-
 head/contrib/tcsh/nls/finnish/set29                                            |     4 +-
 head/contrib/tcsh/nls/finnish/set3                                             |   142 +-
 head/contrib/tcsh/nls/finnish/set6                                             |    10 +-
 head/contrib/tcsh/nls/finnish/set7                                             |    34 +-
 head/contrib/tcsh/nls/finnish/set9                                             |    10 +-
 head/contrib/tcsh/nls/french/charset                                           |     4 +-
 head/contrib/tcsh/nls/french/set1                                              |   124 +-
 head/contrib/tcsh/nls/french/set10                                             |     6 +-
 head/contrib/tcsh/nls/french/set11                                             |     6 +-
 head/contrib/tcsh/nls/french/set12                                             |     4 +-
 head/contrib/tcsh/nls/french/set13                                             |    16 +-
 head/contrib/tcsh/nls/french/set15                                             |     4 +-
 head/contrib/tcsh/nls/french/set16                                             |     6 +-
 head/contrib/tcsh/nls/french/set17                                             |    12 +-
 head/contrib/tcsh/nls/french/set18                                             |     4 +-
 head/contrib/tcsh/nls/french/set19                                             |    28 +-
 head/contrib/tcsh/nls/french/set2                                              |   102 +-
 head/contrib/tcsh/nls/french/set20                                             |    60 +-
 head/contrib/tcsh/nls/french/set21                                             |     6 +-
 head/contrib/tcsh/nls/french/set22                                             |    14 +-
 head/contrib/tcsh/nls/french/set23                                             |    22 +-
 head/contrib/tcsh/nls/french/set25                                             |     4 +-
 head/contrib/tcsh/nls/french/set26                                             |    16 +-
 head/contrib/tcsh/nls/french/set27                                             |     6 +-
 head/contrib/tcsh/nls/french/set3                                              |   192 +-
 head/contrib/tcsh/nls/french/set30                                             |    12 +-
 head/contrib/tcsh/nls/french/set31                                             |     6 +-
 head/contrib/tcsh/nls/french/set4                                              |    30 +-
 head/contrib/tcsh/nls/french/set6                                              |     6 +-
 head/contrib/tcsh/nls/french/set7                                              |    20 +-
 head/contrib/tcsh/nls/french/set8                                              |     4 +-
 head/contrib/tcsh/nls/french/set9                                              |    16 +-
 head/contrib/tcsh/nls/german/charset                                           |     4 +-
 head/contrib/tcsh/nls/german/set1                                              |    66 +-
 head/contrib/tcsh/nls/german/set10                                             |     8 +-
 head/contrib/tcsh/nls/german/set13                                             |     4 +-
 head/contrib/tcsh/nls/german/set15                                             |     4 +-
 head/contrib/tcsh/nls/german/set16                                             |     6 +-
 head/contrib/tcsh/nls/german/set17                                             |     6 +-
 head/contrib/tcsh/nls/german/set18                                             |     4 +-
 head/contrib/tcsh/nls/german/set19                                             |    12 +-
 head/contrib/tcsh/nls/german/set2                                              |    22 +-
 head/contrib/tcsh/nls/german/set20                                             |    10 +-
 head/contrib/tcsh/nls/german/set22                                             |    10 +-
 head/contrib/tcsh/nls/german/set23                                             |    16 +-
 head/contrib/tcsh/nls/german/set25                                             |     4 +-
 head/contrib/tcsh/nls/german/set26                                             |     6 +-
 head/contrib/tcsh/nls/german/set27                                             |     4 +-
 head/contrib/tcsh/nls/german/set29                                             |     4 +-
 head/contrib/tcsh/nls/german/set3                                              |   126 +-
 head/contrib/tcsh/nls/german/set30                                             |     8 +-
 head/contrib/tcsh/nls/german/set31                                             |     8 +-
 head/contrib/tcsh/nls/german/set4                                              |    36 +-
 head/contrib/tcsh/nls/german/set5                                              |     4 +-
 head/contrib/tcsh/nls/german/set6                                              |     6 +-
 head/contrib/tcsh/nls/german/set7                                              |    30 +-
 head/contrib/tcsh/nls/german/set8                                              |     4 +-
 head/contrib/tcsh/nls/german/set9                                              |     6 +-
 head/contrib/tcsh/nls/greek/charset                                            |     4 +-
 head/contrib/tcsh/nls/greek/set1                                               |   270 +-
 head/contrib/tcsh/nls/greek/set10                                              |    12 +-
 head/contrib/tcsh/nls/greek/set11                                              |    16 +-
 head/contrib/tcsh/nls/greek/set12                                              |     4 +-
 head/contrib/tcsh/nls/greek/set13                                              |    20 +-
 head/contrib/tcsh/nls/greek/set14                                              |     8 +-
 head/contrib/tcsh/nls/greek/set15                                              |    10 +-
 head/contrib/tcsh/nls/greek/set16                                              |     8 +-
 head/contrib/tcsh/nls/greek/set17                                              |    24 +-
 head/contrib/tcsh/nls/greek/set18                                              |     4 +-
 head/contrib/tcsh/nls/greek/set19                                              |    34 +-
 head/contrib/tcsh/nls/greek/set2                                               |   162 +-
 head/contrib/tcsh/nls/greek/set20                                              |    78 +-
 head/contrib/tcsh/nls/greek/set21                                              |     6 +-
 head/contrib/tcsh/nls/greek/set22                                              |    28 +-
 head/contrib/tcsh/nls/greek/set23                                              |    48 +-
 head/contrib/tcsh/nls/greek/set25                                              |     6 +-
 head/contrib/tcsh/nls/greek/set26                                              |    22 +-
 head/contrib/tcsh/nls/greek/set27                                              |     8 +-
 head/contrib/tcsh/nls/greek/set29                                              |     4 +-
 head/contrib/tcsh/nls/greek/set3                                               |   230 +-
 head/contrib/tcsh/nls/greek/set30                                              |    22 +-
 head/contrib/tcsh/nls/greek/set31                                              |    10 +-
 head/contrib/tcsh/nls/greek/set4                                               |    86 +-
 head/contrib/tcsh/nls/greek/set5                                               |     4 +-
 head/contrib/tcsh/nls/greek/set6                                               |    14 +-
 head/contrib/tcsh/nls/greek/set7                                               |    56 +-
 head/contrib/tcsh/nls/greek/set8                                               |     6 +-
 head/contrib/tcsh/nls/greek/set9                                               |    20 +-
 head/contrib/tcsh/nls/italian/charset                                          |     4 +-
 head/contrib/tcsh/nls/italian/set1                                             |    18 +-
 head/contrib/tcsh/nls/italian/set11                                            |     4 +-
 head/contrib/tcsh/nls/italian/set13                                            |     6 +-
 head/contrib/tcsh/nls/italian/set15                                            |     4 +-
 head/contrib/tcsh/nls/italian/set17                                            |     8 +-
 head/contrib/tcsh/nls/italian/set19                                            |    12 +-
 head/contrib/tcsh/nls/italian/set2                                             |    16 +-
 head/contrib/tcsh/nls/italian/set20                                            |    14 +-
 head/contrib/tcsh/nls/italian/set22                                            |     8 +-
 head/contrib/tcsh/nls/italian/set23                                            |     4 +-
 head/contrib/tcsh/nls/italian/set26                                            |    14 +-
 head/contrib/tcsh/nls/italian/set3                                             |    34 +-
 head/contrib/tcsh/nls/italian/set30                                            |     4 +-
 head/contrib/tcsh/nls/italian/set4                                             |    24 +-
 head/contrib/tcsh/nls/italian/set6                                             |     4 +-
 head/contrib/tcsh/nls/italian/set7                                             |    18 +-
 head/contrib/tcsh/nls/ja/charset                                               |     4 +-
 head/contrib/tcsh/nls/ja/set1                                                  |   272 +-
 head/contrib/tcsh/nls/ja/set10                                                 |    12 +-
 head/contrib/tcsh/nls/ja/set11                                                 |    16 +-
 head/contrib/tcsh/nls/ja/set12                                                 |     4 +-
 head/contrib/tcsh/nls/ja/set13                                                 |    18 +-
 head/contrib/tcsh/nls/ja/set15                                                 |    10 +-
 head/contrib/tcsh/nls/ja/set16                                                 |    10 +-
 head/contrib/tcsh/nls/ja/set17                                                 |    24 +-
 head/contrib/tcsh/nls/ja/set18                                                 |     4 +-
 head/contrib/tcsh/nls/ja/set2                                                  |   184 +-
 head/contrib/tcsh/nls/ja/set21                                                 |     6 +-
 head/contrib/tcsh/nls/ja/set29                                                 |     4 +-
 head/contrib/tcsh/nls/ja/set3                                                  |   220 +-
 head/contrib/tcsh/nls/ja/set30                                                 |    22 +-
 head/contrib/tcsh/nls/ja/set4                                                  |    84 +-
 head/contrib/tcsh/nls/ja/set5                                                  |     4 +-
 head/contrib/tcsh/nls/ja/set6                                                  |    18 +-
 head/contrib/tcsh/nls/ja/set7                                                  |    56 +-
 head/contrib/tcsh/nls/ja/set8                                                  |     6 +-
 head/contrib/tcsh/nls/russian/charset                                          |     4 +-
 head/contrib/tcsh/nls/russian/set1                                             |   272 +-
 head/contrib/tcsh/nls/russian/set10                                            |    12 +-
 head/contrib/tcsh/nls/russian/set11                                            |    16 +-
 head/contrib/tcsh/nls/russian/set12                                            |     4 +-
 head/contrib/tcsh/nls/russian/set13                                            |    16 +-
 head/contrib/tcsh/nls/russian/set14                                            |     8 +-
 head/contrib/tcsh/nls/russian/set15                                            |    10 +-
 head/contrib/tcsh/nls/russian/set16                                            |     6 +-
 head/contrib/tcsh/nls/russian/set17                                            |    14 +-
 head/contrib/tcsh/nls/russian/set18                                            |     4 +-
 head/contrib/tcsh/nls/russian/set19                                            |    16 +-
 head/contrib/tcsh/nls/russian/set2                                             |    50 +-
 head/contrib/tcsh/nls/russian/set20                                            |    78 +-
 head/contrib/tcsh/nls/russian/set22                                            |    28 +-
 head/contrib/tcsh/nls/russian/set23                                            |    28 +-
 head/contrib/tcsh/nls/russian/set25                                            |     6 +-
 head/contrib/tcsh/nls/russian/set26                                            |    16 +-
 head/contrib/tcsh/nls/russian/set27                                            |    12 +-
 head/contrib/tcsh/nls/russian/set29                                            |     4 +-
 head/contrib/tcsh/nls/russian/set30                                            |    18 +-
 head/contrib/tcsh/nls/russian/set31                                            |    10 +-
 head/contrib/tcsh/nls/russian/set4                                             |    46 +-
 head/contrib/tcsh/nls/russian/set5                                             |     4 +-
 head/contrib/tcsh/nls/russian/set6                                             |    18 +-
 head/contrib/tcsh/nls/russian/set7                                             |    20 +-
 head/contrib/tcsh/nls/russian/set8                                             |     6 +-
 head/contrib/tcsh/nls/russian/set9                                             |     8 +-
 head/contrib/tcsh/nls/spanish/charset                                          |     4 +-
 head/contrib/tcsh/nls/spanish/set1                                             |   122 +-
 head/contrib/tcsh/nls/spanish/set10                                            |     4 +-
 head/contrib/tcsh/nls/spanish/set13                                            |     4 +-
 head/contrib/tcsh/nls/spanish/set14                                            |     4 +-
 head/contrib/tcsh/nls/spanish/set15                                            |     4 +-
 head/contrib/tcsh/nls/spanish/set16                                            |     4 +-
 head/contrib/tcsh/nls/spanish/set17                                            |     8 +-
 head/contrib/tcsh/nls/spanish/set18                                            |     4 +-
 head/contrib/tcsh/nls/spanish/set19                                            |    26 +-
 head/contrib/tcsh/nls/spanish/set2                                             |    96 +-
 head/contrib/tcsh/nls/spanish/set20                                            |    40 +-
 head/contrib/tcsh/nls/spanish/set22                                            |    22 +-
 head/contrib/tcsh/nls/spanish/set23                                            |    28 +-
 head/contrib/tcsh/nls/spanish/set25                                            |     4 +-
 head/contrib/tcsh/nls/spanish/set26                                            |     8 +-
 head/contrib/tcsh/nls/spanish/set27                                            |     6 +-
 head/contrib/tcsh/nls/spanish/set3                                             |   148 +-
 head/contrib/tcsh/nls/spanish/set30                                            |     8 +-
 head/contrib/tcsh/nls/spanish/set4                                             |    60 +-
 head/contrib/tcsh/nls/spanish/set5                                             |     4 +-
 head/contrib/tcsh/nls/spanish/set6                                             |     4 +-
 head/contrib/tcsh/nls/spanish/set7                                             |    26 +-
 head/contrib/tcsh/nls/spanish/set8                                             |     4 +-
 head/contrib/tcsh/nls/spanish/set9                                             |    12 +-
 head/contrib/tcsh/nls/ukrainian/charset                                        |     4 +-
 head/contrib/tcsh/nls/ukrainian/set1                                           |   248 +-
 head/contrib/tcsh/nls/ukrainian/set10                                          |    12 +-
 head/contrib/tcsh/nls/ukrainian/set11                                          |    16 +-
 head/contrib/tcsh/nls/ukrainian/set12                                          |     4 +-
 head/contrib/tcsh/nls/ukrainian/set13                                          |    18 +-
 head/contrib/tcsh/nls/ukrainian/set14                                          |     8 +-
 head/contrib/tcsh/nls/ukrainian/set15                                          |    10 +-
 head/contrib/tcsh/nls/ukrainian/set16                                          |     6 +-
 head/contrib/tcsh/nls/ukrainian/set17                                          |    26 +-
 head/contrib/tcsh/nls/ukrainian/set18                                          |     4 +-
 head/contrib/tcsh/nls/ukrainian/set19                                          |    22 +-
 head/contrib/tcsh/nls/ukrainian/set2                                           |    56 +-
 head/contrib/tcsh/nls/ukrainian/set20                                          |    12 +-
 head/contrib/tcsh/nls/ukrainian/set22                                          |    20 +-
 head/contrib/tcsh/nls/ukrainian/set23                                          |    12 +-
 head/contrib/tcsh/nls/ukrainian/set25                                          |     6 +-
 head/contrib/tcsh/nls/ukrainian/set26                                          |    18 +-
 head/contrib/tcsh/nls/ukrainian/set27                                          |    12 +-
 head/contrib/tcsh/nls/ukrainian/set29                                          |     4 +-
 head/contrib/tcsh/nls/ukrainian/set30                                          |    12 +-
 head/contrib/tcsh/nls/ukrainian/set31                                          |    10 +-
 head/contrib/tcsh/nls/ukrainian/set5                                           |     4 +-
 head/contrib/tcsh/nls/ukrainian/set6                                           |     8 +-
 head/contrib/tcsh/nls/ukrainian/set7                                           |    12 +-
 head/contrib/tcsh/nls/ukrainian/set8                                           |     6 +-
 head/contrib/tcsh/nls/ukrainian/set9                                           |     4 +-
 head/contrib/tcsh/patchlevel.h                                                 |     8 +-
 head/contrib/tcsh/pathnames.h                                                  |    10 +-
 head/contrib/tcsh/sh.c                                                         |   221 +-
 head/contrib/tcsh/sh.char.c                                                    |     6 +-
 head/contrib/tcsh/sh.char.h                                                    |    12 +-
 head/contrib/tcsh/sh.decls.h                                                   |    37 +-
 head/contrib/tcsh/sh.dir.c                                                     |    20 +-
 head/contrib/tcsh/sh.dol.c                                                     |    65 +-
 head/contrib/tcsh/sh.err.c                                                     |   166 +-
 head/contrib/tcsh/sh.exec.c                                                    |    54 +-
 head/contrib/tcsh/sh.exp.c                                                     |   111 +-
 head/contrib/tcsh/sh.file.c                                                    |     6 +-
 head/contrib/tcsh/sh.func.c                                                    |   138 +-
 head/contrib/tcsh/sh.glob.c                                                    |    24 +-
 head/contrib/tcsh/sh.h                                                         |    88 +-
 head/contrib/tcsh/sh.hist.c                                                    |  1169 +-
 head/contrib/tcsh/sh.lex.c                                                     |    32 +-
 head/contrib/tcsh/sh.misc.c                                                    |     6 +-
 head/contrib/tcsh/sh.parse.c                                                   |     7 +-
 head/contrib/tcsh/sh.print.c                                                   |    31 +-
 head/contrib/tcsh/sh.proc.c                                                    |    44 +-
 head/contrib/tcsh/sh.proc.h                                                    |     4 +-
 head/contrib/tcsh/sh.sem.c                                                     |    73 +-
 head/contrib/tcsh/sh.set.c                                                     |   100 +-
 head/contrib/tcsh/sh.time.c                                                    |    85 +-
 head/contrib/tcsh/tc.alloc.c                                                   |    47 +-
 head/contrib/tcsh/tc.const.c                                                   |    21 +-
 head/contrib/tcsh/tc.decls.h                                                   |     8 +-
 head/contrib/tcsh/tc.disc.c                                                    |     6 +-
 head/contrib/tcsh/tc.func.c                                                    |    39 +-
 head/contrib/tcsh/tc.nls.c                                                     |    32 +-
 head/contrib/tcsh/tc.nls.h                                                     |     4 +-
 head/contrib/tcsh/tc.os.c                                                      |    43 +-
 head/contrib/tcsh/tc.os.h                                                      |    20 +-
 head/contrib/tcsh/tc.prompt.c                                                  |    16 +-
 head/contrib/tcsh/tc.sig.c                                                     |    18 +-
 head/contrib/tcsh/tc.sig.h                                                     |     4 +-
 head/contrib/tcsh/tc.str.c                                                     |   101 +-
 head/contrib/tcsh/tc.wait.h                                                    |     4 +-
 head/contrib/tcsh/tc.who.c                                                     |   172 +-
 head/contrib/tcsh/tcsh.man                                                     |   230 +-
 head/contrib/tcsh/tcsh.man2html                                                |     8 +-
 head/contrib/tcsh/tw.color.c                                                   |     7 +-
 head/contrib/tcsh/tw.init.c                                                    |    17 +-
 head/contrib/tcsh/tw.parse.c                                                   |   142 +-
 head/contrib/tcsh/vms.termcap.c                                                |    29 +-
 head/contrib/top/top.c                                                         |    18 +-
 540 files changed, 32141 insertions(+), 21017 deletions(-)

diffs (82661 lines):

diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/gcc/gcc.c
--- a/head/contrib/gcc/gcc.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/gcc/gcc.c	Fri Mar 02 16:54:40 2012 +0200
@@ -23,7 +23,7 @@
 This paragraph is here to try to keep Sun CC from dying.
 The number of chars here seems crucial!!!!  */
 
-/* $FreeBSD$ */
+/* $FreeBSD: head/contrib/gcc/gcc.c 231336 2012-02-10 05:05:42Z kientzle $ */
 
 /* This program is the user interface to the C compiler and possibly to
 other compilers.  It is used because compilation is a complicated procedure
@@ -2696,6 +2696,17 @@
     return xstrdup (DEFAULT_LINKER);
 #endif
 
+#ifdef FREEBSD_NATIVE
+  if (! strcmp(name, "include"))
+    {
+#ifdef CROSS_INCLUDE_DIR
+      return xstrdup(CROSS_INCLUDE_DIR);
+#else
+      return xstrdup(STANDARD_INCLUDE_DIR);
+#endif
+    }
+#endif
+
   /* Determine the filename to execute (special case for absolute paths).  */
 
   if (IS_ABSOLUTE_PATH (name))
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/COPYING
--- a/head/contrib/libarchive/COPYING	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/COPYING	Fri Mar 02 16:54:40 2012 +0200
@@ -16,8 +16,8 @@
   a 3-clause UC Regents copyright; please read the individual source
   files for details:
    libarchive/archive_entry.c
-   libarchive/archive_read_support_compression_compress.c
-   libarchive/archive_write_set_compression_compress.c
+   libarchive/archive_read_support_filter_compress.c
+   libarchive/archive_write_set_filter_compress.c
    libarchive/mtree.5
    tar/matching.c
 
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/FREEBSD-Xlist
--- a/head/contrib/libarchive/FREEBSD-Xlist	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/FREEBSD-Xlist	Fri Mar 02 16:54:40 2012 +0200
@@ -1,4 +1,4 @@
-$FreeBSD: head/contrib/libarchive/FREEBSD-Xlist 228770 2011-12-21 14:58:44Z mm $
+$FreeBSD: head/contrib/libarchive/FREEBSD-Xlist 232153 2012-02-25 10:58:02Z mm $
 CMakeLists.txt
 CTestConfig.cmake
 INSTALL
@@ -15,8 +15,10 @@
 examples
 libarchive/CMakeLists.txt
 libarchive/archive_entry_copy_bhfi.c
+libarchive/archive_read_disk_windows.c
 libarchive/archive_windows.c
 libarchive/archive_windows.h
+libarchive/archive_write_disk_windows.c
 libarchive/config_freebsd.h
 libarchive/filter_fork_windows.c
 libarchive/mtree.5
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/NEWS
--- a/head/contrib/libarchive/NEWS	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/NEWS	Fri Mar 02 16:54:40 2012 +0200
@@ -1,27 +1,80 @@
-Feb 05, 2011: Fix issue 134: Improve handling of open failures
-Dec 06, 2010: Fix issue 119: Relax ISO verification
-Dec 06, 2010: Fix issue 121: mtree parsing
-Dec 05, 2010: Fix extraction of GNU tar 'D' directory entries
-Dec 05, 2010: Be less demanding in LZMA/XZ compression tests
-Jun 30, 2010: libarchive 2.8.4 released
-Jun 30, 2010: Improved reliability of hash function detection
-Jun 30, 2010: Fix issues on ancient FreeBSD, QNX, ancient NetBSD and Minix
+Jan 10, 2012: Issue 223: Skip atime tests if atime not supported
+Jan 09, 2012: Issue 222: Errors saving sparse files to pax archives
+Jan 09, 2012: Issue 221: allow archive_*_free(NULL)
+Dec 31, 2011: Issue 212: configure script on Solaris
+Dec 30, 2011: Issue 218: empty contents extracting Zip files with bsdcpio
+Dec 30, 2011: Issue 217: fix compile warning
+Dec 30, 2011: Issue 216: truncated filenames in listings
+Dec 28, 2011: Issue 210: memory leak on Windows
+Dec 28, 2011: Issue 206: fix hardlink tests on Windows 2000
+Dec 27, 2011: Issue 208: Don't hang when using external compression
+   program on Windows
 
-Mar 14, 2010: libarchive 2.8.3 released
-Mar 14, 2010: Symlink dereference fix for Linux broke the build there; corrected.
+Dec 24, 2011: libarchive 3.0.2 released
+Dec 23, 2011: Various fixes merged from FreeBSD
+Dec 23, 2011: Symlink support in Zip reader and writer
+Dec 23, 2011: Robustness fixes to 7Zip reader
 
-Mar 14, 2010: libarchive 2.8.2 released
-Mar 12, 2010: Fix NULL deference for short self-extracting zip archives.
+Nov 27, 2011: libarchive 3.0.1b released
+
+Nov 26, 2011: 7Zip reader
+Nov 26, 2011: Small fixes to ISO and Zip to improve robustness with corrupted input
+Nov 24, 2011: Improve streaming Zip reader's support for uncompressed entries
+Nov 20, 2011: New seeking Zip reader supports SFX Zip archives
+Nov 20, 2011: Build fixes on Windows
+
+Nov 13, 2011: libarchive 3.0.0a released
+
+Nov 06, 2011: Update shared-library version calculations for libarchive 3.x
+Sep 04, 2011: Fix tar -s; follow GNU tar for controlling hardlink/symlink substitutions
+Aug 18, 2011: Fix reading ISO images built by NetBSD's mkisofs
+Aug 15, 2011: Old archive_read_support_compression_XXX functions are deprecated and
+   will disappear in libarchive 4.0.
+Jun 26, 2011: RAR reader
+Jun 16, 2011: Add tar:compat-2x option to emulate broken libarchive 2.x
+   handling of pax UTF-8 headers
+Apr 25, 2011: Refactor read_open() into a collection of single-item setters;
+   support the old interfaces as wrappers
+Apr 12, 2011: Split disk writer into separate POSIX and Windows implementations
+Apr 10, 2011: Improvements to character translations on Windows.
+Mar 30, 2011: More work to return errors instead of calling abort()
+Mar 23, 2011: Add charset option to many writers to control MBCS filenames
+Mar 17, 2011: Overhauled support for per-format extension options
+Mar 17, 2011: Track character set used for mbcs strings, support
+   translating to/from user-specified locale
+Mar 09, 2011: Recognize mtree files without requiring a signature
+Mar 06, 2011: Use iconv to convert to/from Unicode instead of making bad
+   assumptions about the C90 character set translation functions
+Feb 17, 2011: Fixes for AIX, TRU64, and other platforms
+Dec 22, 2010: CAB reader
+Dec 20, 2010: LHA/LZH reader
+Jul 03, 2010: minitar example demonstrates archive_read_disk directory traversal
+Jun 29, 2010: Many improvements to ISO reader compatibility
+Jun 26, 2010: Use larger buffers when copy files into an archive
+Jun 18, 2010: Reimplement Mac OS extensions in libarchive
+Jun 09, 2010: archive_read_disk now supports traversals
+May 28, 2010: XAR writer
+May 16, 2010: Fix ^T handling; don't exit on interrupted reads and writes
+May 09, 2010: Improved detection of platform-specific crypto support
+May 04, 2010: lzip read and write filters
+May 01, 2010: New options: tar --gid --gname --uid --uname
+Apr 28, 2010: Use Red-black tree for ISO reader/writer to improve performance
+Apr 17, 2010: Minimal writer for legacy GNU tar format
 Mar 12, 2010: Don't dereference symlinks on Linux when reading ACLs.
-Mar 07, 2010: Better detection of SHA2 support for old OpenSSL versions.
-Mar 07, 2010: Fix parsing of input files for bsdtar -T.
-Mar 07, 2010: Do not leak setup_xattr into the global namespace.
-
-Mar 06, 2010: libarchive 2.8.1 released
 Mar 06, 2010: Fix build when an older libarchive is already installed
-Mar 03, 2010: Use O_BINARY opening files in bsdtar
-Mar 02, 2010: Include missing archive_crc32.h
-Mar 01, 2010: Correctly include iconv.h required by libxml2.
+Feb 28, 2010: Relax handling of state failures; misuse by clients now generally
+      results in a sticky ARCHIVE_FATAL rather than a visit to abort()
+Feb 25, 2010: ISO writer
+Feb 21, 2010: Split many man pages into smaller chunks.
+Feb 21, 2010: Performance: Cheat on block sizes when reading archives from disk.
+Feb 21, 2010: Use int64_t instead of off_t, dev_t, ino_t, uid_t, and gid_t
+Feb 20, 2010: Document new ACL functions.
+Feb 19, 2010: Support multiple write filters
+Feb 07, 2010: Remove some legacy libarchive 1.x APIs
+Feb 04, 2010: Read afio headers
+Feb 02, 2010: Archive sparse files compatibly with GNU tar
+Feb 01, 2010: Integrate Apple extensions for Mac OS extended attributes into bsdtar
+Jan 31, 2010: Support cpio -V
 
 Feb 04, 2010: libarchive 2.8.0 released
 Jan 17, 2010: Fix error handling for 'echo nonexistent | cpio -o'
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/README
--- a/head/contrib/libarchive/README	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/README	Fri Mar 02 16:54:40 2012 +0200
@@ -13,8 +13,6 @@
           essentially the same functionality
    * examples: Some small example programs that you may find useful.
    * examples/minitar: a compact sample demonstrating use of libarchive.
-          I use this for testing link pollution; it should produce a very
-          small executable file on most systems.
    * contrib:  Various items sent to me by third parties;
           please contact the authors with any questions.
 
@@ -51,16 +49,11 @@
 a number of different formats.
 
 You should also read the copious comments in "archive.h" and the
-source code for the sample programs for more details.  Please let me
+source code for the sample programs for more details.  Please let us
 know about any errors or omissions you find.
 
-Currently, the library automatically detects and reads the following:
-  * gzip compression
-  * bzip2 compression
-  * compress/LZW compression
-  * lzma and xz compression
-  * GNU tar format (including GNU long filenames, long link names, and
-    sparse files)
+Currently, the library automatically detects and reads the following fomats:
+  * GNU tar format (including GNU long filenames, long link names, and sparse files)
   * Solaris 9 extended tar format (including ACLs)
   * Old V7 tar archives
   * POSIX ustar
@@ -73,22 +66,40 @@
   * ZIP archives (with uncompressed or "deflate" compressed entries)
   * GNU and BSD 'ar' archives
   * 'mtree' format
+  * Microsoft CAB format
+  * LHA and LZH archives
+  * RAR archives
+  * XAR archives
 
-The library can write:
+The library also detects and handles any of the following before evaluating the archive:
+  * uuencoded files
+  * files with RPM wrapper
   * gzip compression
   * bzip2 compression
   * compress/LZW compression
-  * lzma and xz compression
+  * lzma, lzip, and xz compression
+
+The library can create archives in any of the following formats:
   * POSIX ustar
   * POSIX pax interchange format
   * "restricted" pax format, which will create ustar archives except for
     entries that require pax extensions (for long filenames, ACLs, etc).
+  * Old GNU tar format
   * POSIX octet-oriented cpio
   * SVR4 "newc" cpio
   * shar archives
   * ZIP archives (with uncompressed or "deflate" compressed entries)
   * GNU and BSD 'ar' archives
   * 'mtree' format
+  * ISO9660 format
+  * XAR archives
+
+When creating archives, the result can be filtered with any of the following:
+  * uuencode
+  * gzip compression
+  * bzip2 compression
+  * compress/LZW compression
+  * lzma, lzip, and xz compression
 
 Notes about the library architecture:
 
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/bsdcpio.1
--- a/head/contrib/libarchive/cpio/bsdcpio.1	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/bsdcpio.1	Fri Mar 02 16:54:40 2012 +0200
@@ -22,10 +22,10 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: head/contrib/libarchive/cpio/bsdcpio.1 228777 2011-12-21 15:33:13Z mm $
+.\" $FreeBSD: head/contrib/libarchive/cpio/bsdcpio.1 232153 2012-02-25 10:58:02Z mm $
 .\"
-.Dd September 5, 2010
-.Dt BSDCPIO 1
+.Dd December 21, 2007
+.Dt CPIO 1
 .Os
 .Sh NAME
 .Nm cpio
@@ -59,7 +59,7 @@
 .Bl -tag -compact -width indent
 .It Fl i
 Input.
-Read an archive from standard input (unless overriden) and extract the
+Read an archive from standard input (unless overridden) and extract the
 contents to disk or (if the
 .Fl t
 option is specified)
@@ -69,7 +69,7 @@
 .It Fl o
 Output.
 Read a list of filenames from standard input and produce a new archive
-on standard output (unless overriden) containing the specified items.
+on standard output (unless overridden) containing the specified items.
 .It Fl p
 Pass-through.
 Read a list of filenames from standard input and copy the files to the
@@ -80,7 +80,7 @@
 Unless specifically stated otherwise, options are applicable in
 all operating modes.
 .Bl -tag -width indent
-.It Fl 0
+.It Fl 0 , Fl Fl null
 Read filenames separated by NUL characters instead of newlines.
 This is necessary if any of the filenames being read might contain newlines.
 .It Fl A
@@ -102,8 +102,8 @@
 (o mode only)
 Use the old POSIX portable character format.
 Equivalent to
-.Fl -format Ar odc .
-.It Fl d
+.Fl Fl format Ar odc .
+.It Fl d , Fl Fl make-directories
 (i and p modes)
 Create directories as necessary.
 .It Fl E Ar file
@@ -111,14 +111,14 @@
 Read list of file name patterns from
 .Ar file
 to list and extract.
-.It Fl F Ar file
+.It Fl F Ar file , Fl Fl file Ar file
 Read archive from or write archive to
 .Ar file .
 .It Fl f Ar pattern
 (i mode only)
 Ignore files that match
 .Ar pattern .
-.It Fl -format Ar format
+.It Fl H Ar format , Fl Fl format Ar format
 (o mode only)
 Produce the output archive in the specified format.
 Supported formats include:
@@ -145,24 +145,21 @@
 formats currently supported by the underlying
 .Xr libarchive 3
 library.
-.It Fl H Ar format
-Synonym for
-.Fl -format .
-.It Fl h , Fl -help
+.It Fl h , Fl Fl help
 Print usage information.
 .It Fl I Ar file
 Read archive from
 .Ar file .
-.It Fl i
+.It Fl i , Fl Fl extract
 Input mode.
 See above for description.
-.It Fl -insecure
+.It Fl Fl insecure
 (i and p mode only)
 Disable security checks during extraction or copying.
 This allows extraction via symbolic links and path names containing
 .Sq ..
 in the name.
-.It Fl J
+.It Fl J , Fl Fl xz
 (o mode only)
 Compress the file with xz-compatible compression before writing it.
 In input mode, this option is ignored; xz compression is recognized
@@ -175,20 +172,20 @@
 All symbolic links will be followed.
 Normally, symbolic links are archived and copied as symbolic links.
 With this option, the target of the link will be archived or copied instead.
-.It Fl l
+.It Fl l , Fl Fl link
 (p mode only)
 Create links from the target directory to the original files,
 instead of copying.
-.It Fl lzma
+.It Fl Fl lzma
 (o mode only)
 Compress the file with lzma-compatible compression before writing it.
 In input mode, this option is ignored; lzma compression is recognized
 automatically on input.
-.It Fl m
+.It Fl m , Fl Fl preserve-modification-time
 (i and p modes)
 Set file modification time on created files to match
 those in the source.
-.It Fl n
+.It Fl n , Fl Fl numeric-uid-gid
 (i mode, only with
 .Fl t )
 Display numeric uid and gid.
@@ -197,26 +194,26 @@
 displays the user and group names when they are provided in the
 archive, or looks up the user and group names in the system
 password database.
-.It Fl no-preserve-owner
+.It Fl Fl no-preserve-owner
 (i mode only)
 Do not attempt to restore file ownership.
 This is the default when run by non-root users.
 .It Fl O Ar file
 Write archive to
 .Ar file .
-.It Fl o
+.It Fl o , Fl Fl create
 Output mode.
 See above for description.
-.It Fl p
+.It Fl p , Fl Fl pass-through
 Pass-through mode.
 See above for description.
-.It Fl preserve-owner
+.It Fl Fl preserve-owner
 (i mode only)
 Restore file ownership.
 This is the default when run by the root user.
-.It Fl -quiet
+.It Fl Fl quiet
 Suppress unnecessary messages.
-.It Fl R Oo user Oc Ns Oo : Oc Ns Oo group Oc
+.It Fl R Oo user Oc Ns Oo : Oc Ns Oo group Oc , Fl Fl owner Oo user Oc Ns Oo : Oc Ns Oo group Oc
 Set the owner and/or group on files in the output.
 If group is specified with no user
 (for example,
@@ -244,20 +241,24 @@
 If the line read is blank, the file is skipped.
 If the line contains a single period, the file is processed normally.
 Otherwise, the line is taken to be the new name of the file.
-.It Fl t
+.It Fl t , Fl Fl list
 (i mode only)
 List the contents of the archive to stdout;
 do not restore the contents to disk.
-.It Fl u
+.It Fl u , Fl Fl unconditional
 (i and p modes)
 Unconditionally overwrite existing files.
 Ordinarily, an older file will not overwrite a newer file on disk.
-.It Fl v
+.It Fl V , Fl Fl dot
+Print a dot to stderr for each file as it is processed.
+Superseded by
+.Fl v .
+.It Fl v , Fl Fl verbose
 Print the name of each file to stderr as it is processed.
 With
 .Fl t ,
 provide a detailed listing of each file.
-.It Fl -version
+.It Fl Fl version
 Print the program version information and exit.
 .It Fl y
 (o mode only)
@@ -275,6 +276,8 @@
 In input mode, this option is ignored;
 gzip compression is recognized automatically on input.
 .El
+.Sh EXIT STATUS
+.Ex -std
 .Sh ENVIRONMENT
 The following environment variables affect the execution of
 .Nm :
@@ -290,8 +293,6 @@
 .Xr environ 7
 for more information.
 .El
-.Sh EXIT STATUS
-.Ex -std
 .Sh EXAMPLES
 The
 .Nm
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/cmdline.c
--- a/head/contrib/libarchive/cpio/cmdline.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/cmdline.c	Fri Mar 02 16:54:40 2012 +0200
@@ -26,7 +26,7 @@
 
 
 #include "cpio_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/cmdline.c 228777 2011-12-21 15:33:13Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/cmdline.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -51,7 +51,7 @@
 /*
  * Short options for cpio.  Please keep this sorted.
  */
-static const char *short_options = "0AaBC:cdE:F:f:H:hI:iJjLlmnO:opR:rtuvW:yZz";
+static const char *short_options = "0AaBC:cdE:F:f:H:hI:iJjLlmnO:opR:rtuVvW:yZz";
 
 /*
  * Long options for cpio.  Please keep this sorted.
@@ -62,6 +62,7 @@
 	int equivalent;	/* Equivalent short option. */
 } cpio_longopts[] = {
 	{ "create",			0, 'o' },
+	{ "dot",			0, 'V' },
 	{ "extract",			0, 'i' },
 	{ "file",			1, 'F' },
 	{ "format",             	1, 'H' },
@@ -109,7 +110,7 @@
 	int opt = '?';
 	int required = 0;
 
-	cpio->optarg = NULL;
+	cpio->argument = NULL;
 
 	/* First time through, initialize everything. */
 	if (state == state_start) {
@@ -188,7 +189,7 @@
 				long_prefix = "-W "; /* For clearer errors. */
 			} else {
 				state = state_next_word;
-				cpio->optarg = opt_word;
+				cpio->argument = opt_word;
 			}
 		}
 	}
@@ -202,7 +203,7 @@
 		p = strchr(opt_word, '=');
 		if (p != NULL) {
 			optlength = (size_t)(p - opt_word);
-			cpio->optarg = (char *)(uintptr_t)(p + 1);
+			cpio->argument = (char *)(uintptr_t)(p + 1);
 		} else {
 			optlength = strlen(opt_word);
 		}
@@ -241,9 +242,9 @@
 		/* We've found a unique match; does it need an argument? */
 		if (match->required) {
 			/* Argument required: get next word if necessary. */
-			if (cpio->optarg == NULL) {
-				cpio->optarg = *cpio->argv;
-				if (cpio->optarg == NULL) {
+			if (cpio->argument == NULL) {
+				cpio->argument = *cpio->argv;
+				if (cpio->argument == NULL) {
 					lafe_warnc(0,
 					    "Option %s%s requires an argument",
 					    long_prefix, match->name);
@@ -254,7 +255,7 @@
 			}
 		} else {
 			/* Argument forbidden: fail if there is one. */
-			if (cpio->optarg != NULL) {
+			if (cpio->argument != NULL) {
 				lafe_warnc(0,
 				    "Option %s%s does not allow an argument",
 				    long_prefix, match->name);
@@ -340,7 +341,7 @@
 		} else {
 			char *end;
 			errno = 0;
-			*uid = strtoul(user, &end, 10);
+			*uid = (int)strtoul(user, &end, 10);
 			if (errno || *end != '\0') {
 				snprintf(errbuff, sizeof(errbuff),
 				    "Couldn't lookup user ``%s''", user);
@@ -358,7 +359,7 @@
 		} else {
 			char *end;
 			errno = 0;
-			*gid = strtoul(g, &end, 10);
+			*gid = (int)strtoul(g, &end, 10);
 			if (errno || *end != '\0') {
 				snprintf(errbuff, sizeof(errbuff),
 				    "Couldn't lookup group ``%s''", g);
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/cpio.c
--- a/head/contrib/libarchive/cpio/cpio.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/cpio.c	Fri Mar 02 16:54:40 2012 +0200
@@ -26,7 +26,7 @@
 
 
 #include "cpio_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/cpio.c 228777 2011-12-21 15:33:13Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/cpio.c 232153 2012-02-25 10:58:02Z mm $");
 
 #include <sys/types.h>
 #include <archive.h>
@@ -50,9 +50,15 @@
 #ifdef HAVE_GRP_H
 #include <grp.h>
 #endif
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
 #ifdef HAVE_STDARG_H
 #include <stdarg.h>
 #endif
@@ -69,9 +75,6 @@
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
 #ifdef HAVE_TIME_H
 #include <time.h>
 #endif
@@ -116,6 +119,7 @@
 static void	mode_list(struct cpio *);
 static void	mode_out(struct cpio *);
 static void	mode_pass(struct cpio *, const char *);
+static const char *remove_leading_slash(const char *);
 static int	restore_time(struct cpio *, struct archive_entry *,
 		    const char *, int fd);
 static void	usage(void);
@@ -136,20 +140,34 @@
 	cpio->buff = buff;
 	cpio->buff_size = sizeof(buff);
 
+#if defined(HAVE_SIGACTION) && defined(SIGPIPE)
+	{ /* Ignore SIGPIPE signals. */
+		struct sigaction sa;
+		sigemptyset(&sa.sa_mask);
+		sa.sa_flags = 0;
+		sa.sa_handler = SIG_IGN;
+		sigaction(SIGPIPE, &sa, NULL);
+	}
+#endif
+
 	/* Need lafe_progname before calling lafe_warnc. */
 	if (*argv == NULL)
 		lafe_progname = "bsdcpio";
 	else {
 #if defined(_WIN32) && !defined(__CYGWIN__)
 		lafe_progname = strrchr(*argv, '\\');
-#else
+		if (strrchr(*argv, '/') > lafe_progname)
+#endif
 		lafe_progname = strrchr(*argv, '/');
-#endif
 		if (lafe_progname != NULL)
 			lafe_progname++;
 		else
 			lafe_progname = *argv;
 	}
+#if HAVE_SETLOCALE
+	if (setlocale(LC_ALL, "") == NULL)
+		lafe_warnc(0, "Failed to set default locale");
+#endif
 
 	cpio->uid_override = -1;
 	cpio->gid_override = -1;
@@ -187,9 +205,9 @@
 			cpio->bytes_per_block = 5120;
 			break;
 		case 'C': /* NetBSD/OpenBSD */
-			cpio->bytes_per_block = atoi(cpio->optarg);
+			cpio->bytes_per_block = atoi(cpio->argument);
 			if (cpio->bytes_per_block <= 0)
-				lafe_errc(1, 0, "Invalid blocksize %s", cpio->optarg);
+				lafe_errc(1, 0, "Invalid blocksize %s", cpio->argument);
 			break;
 		case 'c': /* POSIX 1997 */
 			cpio->format = "odc";
@@ -199,22 +217,22 @@
 			break;
 		case 'E': /* NetBSD/OpenBSD */
 			lafe_include_from_file(&cpio->matching,
-			    cpio->optarg, cpio->option_null);
+			    cpio->argument, cpio->option_null);
 			break;
 		case 'F': /* NetBSD/OpenBSD/GNU cpio */
-			cpio->filename = cpio->optarg;
+			cpio->filename = cpio->argument;
 			break;
 		case 'f': /* POSIX 1997 */
-			lafe_exclude(&cpio->matching, cpio->optarg);
+			lafe_exclude(&cpio->matching, cpio->argument);
 			break;
 		case 'H': /* GNU cpio (also --format) */
-			cpio->format = cpio->optarg;
+			cpio->format = cpio->argument;
 			break;
 		case 'h':
 			long_help();
 			break;
 		case 'I': /* NetBSD/OpenBSD */
-			cpio->filename = cpio->optarg;
+			cpio->filename = cpio->argument;
 			break;
 		case 'i': /* POSIX 1997 */
 			if (cpio->mode != '\0')
@@ -251,7 +269,7 @@
 			cpio->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
 			break;
 		case 'O': /* GNU cpio */
-			cpio->filename = cpio->optarg;
+			cpio->filename = cpio->argument;
 			break;
 		case 'o': /* POSIX 1997 */
 			if (cpio->mode != '\0')
@@ -275,7 +293,7 @@
 		case 'R': /* GNU cpio, also --owner */
 			/* TODO: owner_parse should return uname/gname
 			 * also; use that to set [ug]name_override. */
-			errmsg = owner_parse(cpio->optarg, &uid, &gid);
+			errmsg = owner_parse(cpio->argument, &uid, &gid);
 			if (errmsg) {
 				lafe_warnc(-1, "%s", errmsg);
 				usage();
@@ -302,6 +320,9 @@
 		case 'v': /* POSIX 1997 */
 			cpio->verbose++;
 			break;
+		case 'V': /* GNU cpio */
+			cpio->dot++;
+			break;
 		case OPTION_VERSION: /* GNU convention */
 			version();
 			break;
@@ -345,6 +366,12 @@
 	/* -l requires -p */
 	if (cpio->option_link && cpio->mode != 'p')
 		lafe_errc(1, 0, "Option -l requires -p");
+	/* -v overrides -V */
+	if (cpio->dot && cpio->verbose)
+		cpio->dot = 0;
+	/* -v overrides -V */
+	if (cpio->dot && cpio->verbose)
+		cpio->dot = 0;
 	/* TODO: Flag other nonsensical combinations. */
 
 	switch (cpio->mode) {
@@ -402,7 +429,7 @@
 	"First option must be a mode specifier:\n"
 	"  -i Input  -o Output  -p Pass\n"
 	"Common Options:\n"
-	"  -v    Verbose\n"
+	"  -v Verbose filenames     -V  one dot per file\n"
 	"Create: %p -o [options]  < [list of files] > [archive]\n"
 	"  -J,-y,-z,--lzma  Compress archive with xz/bzip2/gzip/lzma\n"
 	"  --format {odc|newc|ustar}  Select archive format\n"
@@ -451,7 +478,7 @@
 {
 	fprintf(stdout,"bsdcpio %s -- %s\n",
 	    BSDCPIO_VERSION_STRING,
-	    archive_version());
+	    archive_version_string());
 	exit(0);
 }
 
@@ -533,6 +560,8 @@
 	}
 
 	r = archive_write_close(cpio->archive);
+	if (cpio->dot)
+		fprintf(stderr, "\n");
 	if (r != ARCHIVE_OK)
 		lafe_errc(1, 0, "%s", archive_error_string(cpio->archive));
 
@@ -543,7 +572,50 @@
 		fprintf(stderr, "%lu %s\n", (unsigned long)blocks,
 		    blocks == 1 ? "block" : "blocks");
 	}
-	archive_write_finish(cpio->archive);
+	archive_write_free(cpio->archive);
+}
+
+static const char *
+remove_leading_slash(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;
+	}
+	do {
+		rp = p;
+		/* Remove leading drive letter from archives created
+		 * on Windows. */
+		if (((p[0] >= 'a' && p[0] <= 'z') ||
+		     (p[0] >= 'A' && p[0] <= 'Z')) &&
+			 p[1] == ':') {
+			p += 2;
+		}
+		/* 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
+				p += 1; /* Remove "/". */
+		}
+	} while (rp != p);
+	return (p);
 }
 
 /*
@@ -557,7 +629,6 @@
 	const char *destpath;
 	struct archive_entry *entry, *spare;
 	size_t len;
-	const char *p;
 	int r;
 
 	/*
@@ -611,10 +682,7 @@
 				    "Can't allocate path buffer");
 		}
 		strcpy(cpio->pass_destpath, cpio->destdir);
-		p = srcpath;
-		while (p[0] == '/')
-			++p;
-		strcat(cpio->pass_destpath, p);
+		strcat(cpio->pass_destpath, remove_leading_slash(srcpath));
 		destpath = cpio->pass_destpath;
 	}
 	if (cpio->option_rename)
@@ -656,6 +724,8 @@
 	/* 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
@@ -725,7 +795,7 @@
 	if (r == ARCHIVE_FATAL)
 		exit(1);
 
-	if (r >= ARCHIVE_WARN && fd >= 0) {
+	if (r >= ARCHIVE_WARN && archive_entry_size(entry) > 0 && fd >= 0) {
 		bytes_read = read(fd, cpio->buff, cpio->buff_size);
 		while (bytes_read > 0) {
 			r = archive_write_data(cpio->archive,
@@ -825,7 +895,7 @@
 	a = archive_read_new();
 	if (a == NULL)
 		lafe_errc(1, 0, "Couldn't allocate archive object");
-	archive_read_support_compression_all(a);
+	archive_read_support_filter_all(a);
 	archive_read_support_format_all(a);
 
 	if (archive_read_open_file(a, cpio->filename, cpio->bytes_per_block))
@@ -849,7 +919,9 @@
 		if (destpath == NULL)
 			continue;
 		if (cpio->verbose)
-			fprintf(stdout, "%s\n", destpath);
+			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)
@@ -859,13 +931,16 @@
 			fprintf(stderr, "%s: %s\n",
 			    archive_entry_pathname(entry),
 			    archive_error_string(ext));
-		} else if (archive_entry_size(entry) > 0) {
+		} 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);
@@ -877,8 +952,8 @@
 		fprintf(stderr, "%lu %s\n", (unsigned long)blocks,
 		    blocks == 1 ? "block" : "blocks");
 	}
-	archive_read_finish(a);
-	archive_write_finish(ext);
+	archive_read_free(a);
+	archive_write_free(ext);
 	exit(cpio->return_value);
 }
 
@@ -892,7 +967,7 @@
 	int r;
 	size_t size;
 	const void *block;
-	off_t offset;
+	int64_t offset;
 
 	for (;;) {
 		r = archive_read_data_block(ar, &block, &size, &offset);
@@ -922,7 +997,7 @@
 	a = archive_read_new();
 	if (a == NULL)
 		lafe_errc(1, 0, "Couldn't allocate archive object");
-	archive_read_support_compression_all(a);
+	archive_read_support_filter_all(a);
 	archive_read_support_format_all(a);
 
 	if (archive_read_open_file(a, cpio->filename, cpio->bytes_per_block))
@@ -952,7 +1027,7 @@
 		fprintf(stderr, "%lu %s\n", (unsigned long)blocks,
 		    blocks == 1 ? "block" : "blocks");
 	}
-	archive_read_finish(a);
+	archive_read_free(a);
 	exit(0);
 }
 
@@ -989,11 +1064,11 @@
 		/* Use uname if it's present, else lookup name from uid. */
 		uname = archive_entry_uname(entry);
 		if (uname == NULL)
-			uname = lookup_uname(cpio, archive_entry_uid(entry));
+			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, archive_entry_gid(entry));
+			gname = lookup_gname(cpio, (uid_t)archive_entry_gid(entry));
 	}
 
 	/* Print device number or file size. */
@@ -1075,6 +1150,8 @@
 
 	archive_entry_linkresolver_free(cpio->linkresolver);
 	r = archive_write_close(cpio->archive);
+	if (cpio->dot)
+		fprintf(stderr, "\n");
 	if (r != ARCHIVE_OK)
 		lafe_errc(1, 0, "%s", archive_error_string(cpio->archive));
 
@@ -1086,7 +1163,7 @@
 		    blocks == 1 ? "block" : "blocks");
 	}
 
-	archive_write_finish(cpio->archive);
+	archive_write_free(cpio->archive);
 }
 
 /*
@@ -1102,12 +1179,24 @@
 	static char buff[1024];
 	FILE *t;
 	char *p, *ret;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	FILE *to;
 
+	t = fopen("CONIN$", "r");
+	if (t == NULL)
+		return (name);
+	to = fopen("CONOUT$", "w");
+	if (to == NULL)
+		return (name);
+	fprintf(to, "%s (Enter/./(new name))? ", name);
+	fclose(to);
+#else
 	t = fopen("/dev/tty", "r+");
 	if (t == NULL)
 		return (name);
 	fprintf(t, "%s (Enter/./(new name))? ", name);
 	fflush(t);
+#endif
 
 	p = fgets(buff, sizeof(buff), t);
 	fclose(t);
@@ -1260,8 +1349,9 @@
 const char *
 cpio_i64toa(int64_t n0)
 {
-	// 2^64 =~ 1.8 * 10^19, so 20 decimal digits suffice.
-	// We also need 1 byte for '-' and 1 for '\0'.
+	/* 2^64 =~ 1.8 * 10^19, so 20 decimal digits suffice.
+	 * We also need 1 byte for '-' and 1 for '\0'.
+	 */
 	static char buff[22];
 	int64_t n = n0 < 0 ? -n0 : n0;
 	char *p = buff + sizeof(buff);
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/cpio.h
--- a/head/contrib/libarchive/cpio/cpio.h	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/cpio.h	Fri Mar 02 16:54:40 2012 +0200
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/contrib/libarchive/cpio/cpio.h 228777 2011-12-21 15:33:13Z mm $
+ * $FreeBSD: head/contrib/libarchive/cpio/cpio.h 232153 2012-02-25 10:58:02Z mm $
  */
 
 #ifndef CPIO_H_INCLUDED
@@ -43,18 +43,18 @@
  */
 struct cpio {
 	/* Option parsing */
-	const char	 *optarg;
+	const char	 *argument;
 
 	/* Options */
 	const char	 *filename;
-	char		  mode; /* -i -o -p */
-	char		  compress; /* -j, -y, or -z */
+	int		  mode; /* -i -o -p */
+	int		  compress; /* -j, -y, or -z */
 	const char	 *format; /* -H format */
 	int		  bytes_per_block; /* -b block_size */
 	int		  verbose;   /* -v */
+	int		  dot;  /* -V */
 	int		  quiet;   /* --quiet */
 	int		  extract_flags; /* Flags for extract operation */
-	char		  symlink_mode; /* H or L, per BSD conventions */
 	const char	 *compress_program;
 	int		  option_append; /* -A, only relevant for -o */
 	int		  option_atime_restore; /* -a */
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/test/main.c
--- a/head/contrib/libarchive/cpio/test/main.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/test/main.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,8 +24,18 @@
  */
 
 #include "test.h"
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
 #include <errno.h>
+#ifdef HAVE_ICONV_H
+#include <iconv.h>
+#endif
+#include <limits.h>
 #include <locale.h>
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
 #include <stdarg.h>
 #include <time.h>
 
@@ -36,12 +46,14 @@
  * TODO: Move this into a separate configuration header, have all test
  * suites share one copy of this file.
  */
-__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/main.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/main.c 232153 2012-02-25 10:58:02Z mm $");
 #define KNOWNREF	"test_option_f.cpio.uu"
 #define ENVBASE "BSDCPIO" /* Prefix for environment variables. */
 #define	PROGRAM "bsdcpio" /* Name of program being tested. */
-#undef LIBRARY		  /* Not testing a library. */
-#undef	EXTRA_DUMP	     /* How to dump extra data */
+#define PROGRAM_ALIAS "cpio" /* Generic alias for program */
+#undef	LIBRARY		  /* Not testing a library. */
+#undef	EXTRA_DUMP	  /* How to dump extra data */
+#undef	EXTRA_ERRNO	  /* How to dump errno */
 /* How to generate extra version info. */
 #define	EXTRA_VERSION    (systemf("%s --version", testprog) ? "" : "")
 
@@ -78,6 +90,7 @@
 #endif
 #if !defined(__BORLANDC__)
 #define access _access
+#undef chdir
 #define chdir _chdir
 #endif
 #ifndef fileno
@@ -150,7 +163,7 @@
 
 	memset(bhfi, 0, sizeof(*bhfi));
 	h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL,
-		OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+		OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
 	if (h == INVALID_HANDLE_VALUE)
 		return (0);
 	r = GetFileInformationByHandle(h, bhfi);
@@ -179,6 +192,8 @@
 static int dump_on_failure = 0;
 /* Default is to remove temp dirs and log data for successful tests. */
 static int keep_temp_files = 0;
+/* Default is to run the specified tests once and report errors. */
+static int until_failure = 0;
 /* Default is to just report pass/fail for each test. */
 static int verbosity = 0;
 #define	VERBOSITY_SUMMARY_ONLY -1 /* -q */
@@ -236,10 +251,14 @@
 failure(const char *fmt, ...)
 {
 	va_list ap;
-	va_start(ap, fmt);
-	vsprintf(msgbuff, fmt, ap);
-	va_end(ap);
-	nextmsg = msgbuff;
+	if (fmt == NULL) {
+		nextmsg = NULL;
+	} else {
+		va_start(ap, fmt);
+		vsprintf(msgbuff, fmt, ap);
+		va_end(ap);
+		nextmsg = msgbuff;
+	}
 }
 
 /*
@@ -251,15 +270,14 @@
  * pass __FILE__, __LINE__ directly into the function instead of using
  * this hook.  I suspect this machinery is used so rarely that we
  * would be better off just removing it entirely.  That would simplify
- * the code here noticably.
+ * the code here noticeably.
  */
-static const char *test_filename;
-static int test_line;
-static void *test_extra;
-void assertion_setup(const char *filename, int line)
+static const char *skipping_filename;
+static int skipping_line;
+void skipping_setup(const char *filename, int line)
 {
-	test_filename = filename;
-	test_line = line;
+	skipping_filename = filename;
+	skipping_line = line;
 }
 
 /* Called at the beginning of each assert() function. */
@@ -286,6 +304,7 @@
 	int count;
 	int skip;
 }  failed_lines[10000];
+const char *failed_filename;
 
 /* Count this failure, setup up log destination and handle initial report. */
 static void
@@ -295,19 +314,16 @@
 
 	/* Record another failure for this line. */
 	++failures;
-	/* test_filename = filename; */
+	failed_filename = filename;
 	failed_lines[line].count++;
 
 	/* Determine whether to log header to console. */
 	switch (verbosity) {
-	case VERBOSITY_FULL:
-		log_console = 1;
-		break;
 	case VERBOSITY_LIGHT_REPORT:
 		log_console = (failed_lines[line].count < 2);
 		break;
 	default:
-		log_console = 0;
+		log_console = (verbosity >= VERBOSITY_FULL);
 	}
 
 	/* Log file:line header for this failure */
@@ -343,14 +359,16 @@
 {
 	(void)extra; /* UNUSED (maybe) */
 #ifdef EXTRA_DUMP
-	if (extra != NULL)
+	if (extra != NULL) {
+		logprintf("    errno: %d\n", EXTRA_ERRNO(extra));
 		logprintf("   detail: %s\n", EXTRA_DUMP(extra));
+	}
 #endif
 
 	if (dump_on_failure) {
 		fprintf(stderr,
 		    " *** forcing core dump so failure can be debugged ***\n");
-		*(char *)(NULL) = 0;
+		abort();
 		exit(1);
 	}
 }
@@ -365,12 +383,15 @@
 	va_start(ap, fmt);
 	vsprintf(buff, fmt, ap);
 	va_end(ap);
+	/* Use failure() message if set. */
+	msg = nextmsg;
+	nextmsg = NULL;
 	/* failure_start() isn't quite right, but is awfully convenient. */
-	failure_start(test_filename, test_line, "SKIPPING: %s", buff);
+	failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff);
 	--failures; /* Undo failures++ in failure_start() */
 	/* Don't failure_finish() here. */
 	/* Mark as skip, so doesn't count as failed test. */
-	failed_lines[test_line].skip = 1;
+	failed_lines[skipping_line].skip = 1;
 	++skips;
 }
 
@@ -421,13 +442,102 @@
 	return (0);
 }
 
-static void strdump(const char *e, const char *p)
+/*
+ * Utility to convert a single UTF-8 sequence.
+ */
+static int
+_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+	static const char utf8_count[256] = {
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */
+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */
+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */
+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */
+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */
+		 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */
+		 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */
+		 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */
+		 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */
+	};
+	int ch;
+	int cnt;
+	uint32_t wc;
+
+	*pwc = 0;
+
+	/* Sanity check. */
+	if (n == 0)
+		return (0);
+	/*
+	 * Decode 1-4 bytes depending on the value of the first byte.
+	 */
+	ch = (unsigned char)*s;
+	if (ch == 0)
+		return (0); /* Standard:  return 0 for end-of-string. */
+	cnt = utf8_count[ch];
+
+	/* Invalide sequence or there are not plenty bytes. */
+	if (n < (size_t)cnt)
+		return (-1);
+
+	/* Make a Unicode code point from a single UTF-8 sequence. */
+	switch (cnt) {
+	case 1:	/* 1 byte sequence. */
+		*pwc = ch & 0x7f;
+		return (cnt);
+	case 2:	/* 2 bytes sequence. */
+		if ((s[1] & 0xc0) != 0x80) return (-1);
+		*pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
+		return (cnt);
+	case 3:	/* 3 bytes sequence. */
+		if ((s[1] & 0xc0) != 0x80) return (-1);
+		if ((s[2] & 0xc0) != 0x80) return (-1);
+		wc = ((ch & 0x0f) << 12)
+		    | ((s[1] & 0x3f) << 6)
+		    | (s[2] & 0x3f);
+		if (wc < 0x800)
+			return (-1);/* Overlong sequence. */
+		break;
+	case 4:	/* 4 bytes sequence. */
+		if (n < 4)
+			return (-1);
+		if ((s[1] & 0xc0) != 0x80) return (-1);
+		if ((s[2] & 0xc0) != 0x80) return (-1);
+		if ((s[3] & 0xc0) != 0x80) return (-1);
+		wc = ((ch & 0x07) << 18)
+		    | ((s[1] & 0x3f) << 12)
+		    | ((s[2] & 0x3f) << 6)
+		    | (s[3] & 0x3f);
+		if (wc < 0x10000)
+			return (-1);/* Overlong sequence. */
+		break;
+	default:
+		return (-1);
+	}
+
+	/* The code point larger than 0x10FFFF is not leagal
+	 * Unicode values. */
+	if (wc > 0x10FFFF)
+		return (-1);
+	/* Correctly gets a Unicode, returns used bytes. */
+	*pwc = wc;
+	return (cnt);
+}
+
+static void strdump(const char *e, const char *p, int ewidth, int utf8)
 {
 	const char *q = p;
 
-	logprintf("      %s = ", e);
+	logprintf("      %*s = ", ewidth, e);
 	if (p == NULL) {
-		logprintf("NULL");
+		logprintf("NULL\n");
 		return;
 	}
 	logprintf("\"");
@@ -446,7 +556,37 @@
 		}
 	}
 	logprintf("\"");
-	logprintf(" (length %d)\n", q == NULL ? -1 : (int)strlen(q));
+	logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q));
+
+	/*
+	 * If the current string is UTF-8, dump its code points.
+	 */
+	if (utf8) {
+		size_t len;
+		uint32_t uc;
+		int n;
+		int cnt = 0;
+
+		p = q;
+		len = strlen(p);
+		logprintf(" [");
+		while ((n = _utf8_to_unicode(&uc, p, len)) > 0) {
+			if (p != q)
+				logprintf(" ");
+			logprintf("%04X", uc);
+			p += n;
+			len -= n;
+			cnt++;
+		}
+		logprintf("]");
+		logprintf(" (count %d", cnt);
+		if (n < 0) {
+			logprintf(",unknown %d bytes", len);
+		}
+		logprintf(")");
+
+	}
+	logprintf("\n");
 }
 
 /* Verify two strings are equal, dump them if not. */
@@ -454,14 +594,20 @@
 assertion_equal_string(const char *file, int line,
     const char *v1, const char *e1,
     const char *v2, const char *e2,
-    void *extra)
+    void *extra, int utf8)
 {
+	int l1, l2;
+
 	assertion_count(file, line);
 	if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0))
 		return (1);
 	failure_start(file, line, "%s != %s", e1, e2);
-	strdump(e1, v1);
-	strdump(e2, v2);
+	l1 = strlen(e1);
+	l2 = strlen(e2);
+	if (l1 < l2)
+		l1 = l2;
+	strdump(e1, v1, l1, utf8);
+	strdump(e2, v2, l1, utf8);
 	failure_finish(extra);
 	return (0);
 }
@@ -513,7 +659,9 @@
     void *extra)
 {
 	assertion_count(file, line);
-	if (v1 == v2 || wcscmp(v1, v2) == 0)
+	if (v1 == v2)
+		return (1);
+	if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0)
 		return (1);
 	failure_start(file, line, "%s != %s", e1, e2);
 	wcsdump(e1, v1);
@@ -592,9 +740,9 @@
 		offset += 16;
 	}
 	logprintf("      Dump of %s\n", e1);
-	hexdump(v1, v2, l < 64 ? l : 64, offset);
+	hexdump(v1, v2, l < 128 ? l : 128, offset);
 	logprintf("      Dump of %s\n", e2);
-	hexdump(v2, v1, l < 64 ? l : 64, offset);
+	hexdump(v2, v1, l < 128 ? l : 128, offset);
 	logprintf("\n");
 	failure_finish(extra);
 	return (0);
@@ -602,29 +750,24 @@
 
 /* Verify that the named file exists and is empty. */
 int
-assertion_empty_file(const char *f1fmt, ...)
+assertion_empty_file(const char *filename, int line, const char *f1)
 {
 	char buff[1024];
-	char f1[1024];
 	struct stat st;
-	va_list ap;
 	ssize_t s;
 	FILE *f;
 
-	assertion_count(test_filename, test_line);
-	va_start(ap, f1fmt);
-	vsprintf(f1, f1fmt, ap);
-	va_end(ap);
+	assertion_count(filename, line);
 
 	if (stat(f1, &st) != 0) {
-		failure_start(test_filename, test_line, "Stat failed: %s", f1);
+		failure_start(filename, line, "Stat failed: %s", f1);
 		failure_finish(NULL);
 		return (0);
 	}
 	if (st.st_size == 0)
 		return (1);
 
-	failure_start(test_filename, test_line, "File should be empty: %s", f1);
+	failure_start(filename, line, "File should be empty: %s", f1);
 	logprintf("    File size: %d\n", (int)st.st_size);
 	logprintf("    Contents:\n");
 	f = fopen(f1, "rb");
@@ -643,24 +786,19 @@
 
 /* Verify that the named file exists and is not empty. */
 int
-assertion_non_empty_file(const char *f1fmt, ...)
+assertion_non_empty_file(const char *filename, int line, const char *f1)
 {
-	char f1[1024];
 	struct stat st;
-	va_list ap;
 
-	assertion_count(test_filename, test_line);
-	va_start(ap, f1fmt);
-	vsprintf(f1, f1fmt, ap);
-	va_end(ap);
+	assertion_count(filename, line);
 
 	if (stat(f1, &st) != 0) {
-		failure_start(test_filename, test_line, "Stat failed: %s", f1);
+		failure_start(filename, line, "Stat failed: %s", f1);
 		failure_finish(NULL);
 		return (0);
 	}
 	if (st.st_size == 0) {
-		failure_start(test_filename, test_line, "File empty: %s", f1);
+		failure_start(filename, line, "File empty: %s", f1);
 		failure_finish(NULL);
 		return (0);
 	}
@@ -670,19 +808,14 @@
 /* Verify that two files have the same contents. */
 /* TODO: hexdump the first bytes that actually differ. */
 int
-assertion_equal_file(const char *fn1, const char *f2pattern, ...)
+assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2)
 {
-	char fn2[1024];
-	va_list ap;
 	char buff1[1024];
 	char buff2[1024];
 	FILE *f1, *f2;
 	int n1, n2;
 
-	assertion_count(test_filename, test_line);
-	va_start(ap, f2pattern);
-	vsprintf(fn2, f2pattern, ap);
-	va_end(ap);
+	assertion_count(filename, line);
 
 	f1 = fopen(fn1, "rb");
 	f2 = fopen(fn2, "rb");
@@ -701,24 +834,18 @@
 	}
 	fclose(f1);
 	fclose(f2);
-	failure_start(test_filename, test_line, "Files not identical");
+	failure_start(filename, line, "Files not identical");
 	logprintf("  file1=\"%s\"\n", fn1);
 	logprintf("  file2=\"%s\"\n", fn2);
-	failure_finish(test_extra);
+	failure_finish(NULL);
 	return (0);
 }
 
 /* Verify that the named file does exist. */
 int
-assertion_file_exists(const char *fpattern, ...)
+assertion_file_exists(const char *filename, int line, const char *f)
 {
-	char f[1024];
-	va_list ap;
-
-	assertion_count(test_filename, test_line);
-	va_start(ap, fpattern);
-	vsprintf(f, fpattern, ap);
-	va_end(ap);
+	assertion_count(filename, line);
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
 	if (!_access(f, 0))
@@ -727,22 +854,16 @@
 	if (!access(f, F_OK))
 		return (1);
 #endif
-	failure_start(test_filename, test_line, "File should exist: %s", f);
-	failure_finish(test_extra);
+	failure_start(filename, line, "File should exist: %s", f);
+	failure_finish(NULL);
 	return (0);
 }
 
 /* Verify that the named file doesn't exist. */
 int
-assertion_file_not_exists(const char *fpattern, ...)
+assertion_file_not_exists(const char *filename, int line, const char *f)
 {
-	char f[1024];
-	va_list ap;
-
-	assertion_count(test_filename, test_line);
-	va_start(ap, fpattern);
-	vsprintf(f, fpattern, ap);
-	va_end(ap);
+	assertion_count(filename, line);
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
 	if (_access(f, 0))
@@ -751,31 +872,26 @@
 	if (access(f, F_OK))
 		return (1);
 #endif
-	failure_start(test_filename, test_line, "File should not exist: %s", f);
-	failure_finish(test_extra);
+	failure_start(filename, line, "File should not exist: %s", f);
+	failure_finish(NULL);
 	return (0);
 }
 
 /* Compare the contents of a file to a block of memory. */
 int
-assertion_file_contents(const void *buff, int s, const char *fpattern, ...)
+assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn)
 {
-	char fn[1024];
-	va_list ap;
 	char *contents;
 	FILE *f;
 	int n;
 
-	assertion_count(test_filename, test_line);
-	va_start(ap, fpattern);
-	vsprintf(fn, fpattern, ap);
-	va_end(ap);
+	assertion_count(filename, line);
 
 	f = fopen(fn, "rb");
 	if (f == NULL) {
-		failure_start(test_filename, test_line,
+		failure_start(filename, line,
 		    "File should exist: %s", fn);
-		failure_finish(test_extra);
+		failure_finish(NULL);
 		return (0);
 	}
 	contents = malloc(s * 2);
@@ -785,34 +901,34 @@
 		free(contents);
 		return (1);
 	}
-	failure_start(test_filename, test_line, "File contents don't match");
+	failure_start(filename, line, "File contents don't match");
 	logprintf("  file=\"%s\"\n", fn);
 	if (n > 0)
 		hexdump(contents, buff, n > 512 ? 512 : n, 0);
 	else {
 		logprintf("  File empty, contents should be:\n");
-		hexdump(buff, NULL, s > 512 ? 512 : n, 0);
+		hexdump(buff, NULL, s > 512 ? 512 : s, 0);
 	}
-	failure_finish(test_extra);
+	failure_finish(NULL);
 	free(contents);
 	return (0);
 }
 
 /* Check the contents of a text file, being tolerant of line endings. */
 int
-assertion_text_file_contents(const char *buff, const char *fn)
+assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn)
 {
 	char *contents;
 	const char *btxt, *ftxt;
 	FILE *f;
 	int n, s;
 
-	assertion_count(test_filename, test_line);
+	assertion_count(filename, line);
 	f = fopen(fn, "r");
 	if (f == NULL) {
-		failure_start(test_filename, test_line,
+		failure_start(filename, line,
 		    "File doesn't exist: %s", fn);
-		failure_finish(test_extra);
+		failure_finish(NULL);
 		return (0);
 	}
 	s = strlen(buff);
@@ -842,19 +958,122 @@
 		free(contents);
 		return (1);
 	}
-	failure_start(test_filename, test_line, "Contents don't match");
+	failure_start(filename, line, "Contents don't match");
 	logprintf("  file=\"%s\"\n", fn);
-	if (n > 0)
+	if (n > 0) {
 		hexdump(contents, buff, n, 0);
-	else {
+		logprintf("  expected\n", fn);
+		hexdump(buff, contents, s, 0);
+	} else {
 		logprintf("  File empty, contents should be:\n");
 		hexdump(buff, NULL, s, 0);
 	}
-	failure_finish(test_extra);
+	failure_finish(NULL);
 	free(contents);
 	return (0);
 }
 
+/* Verify that a text file contains the specified lines, regardless of order */
+/* This could be more efficient if we sorted both sets of lines, etc, but
+ * since this is used only for testing and only ever deals with a dozen or so
+ * lines at a time, this relatively crude approach is just fine. */
+int
+assertion_file_contains_lines_any_order(const char *file, int line,
+    const char *pathname, const char *lines[])
+{
+	char *buff;
+	size_t buff_size;
+	size_t expected_count, actual_count, i, j;
+	char **expected;
+	char *p, **actual;
+	char c;
+	int expected_failure = 0, actual_failure = 0;
+
+	assertion_count(file, line);
+
+	buff = slurpfile(&buff_size, "%s", pathname);
+	if (buff == NULL) {
+		failure_start(pathname, line, "Can't read file: %s", pathname);
+		failure_finish(NULL);
+		return (0);
+	}
+
+	/* Make a copy of the provided lines and count up the expected file size. */
+	expected_count = 0;
+	for (i = 0; lines[i] != NULL; ++i) {
+	}
+	expected_count = i;
+	expected = malloc(sizeof(char *) * expected_count);
+	for (i = 0; lines[i] != NULL; ++i) {
+		expected[i] = strdup(lines[i]);
+	}
+
+	/* Break the file into lines */
+	actual_count = 0;
+	for (c = '\0', p = buff; p < buff + buff_size; ++p) {
+		if (*p == '\x0d' || *p == '\x0a')
+			*p = '\0';
+		if (c == '\0' && *p != '\0')
+			++actual_count;
+		c = *p;
+	}
+	actual = malloc(sizeof(char *) * actual_count);
+	for (j = 0, p = buff; p < buff + buff_size; p += 1 + strlen(p)) {
+		if (*p != '\0') {
+			actual[j] = p;
+			++j;
+		}
+	}
+
+	/* Erase matching lines from both lists */
+	for (i = 0; i < expected_count; ++i) {
+		if (expected[i] == NULL)
+			continue;
+		for (j = 0; j < actual_count; ++j) {
+			if (actual[j] == NULL)
+				continue;
+			if (strcmp(expected[i], actual[j]) == 0) {
+				free(expected[i]);
+				expected[i] = NULL;
+				actual[j] = NULL;
+				break;
+			}
+		}
+	}
+
+	/* If there's anything left, it's a failure */
+	for (i = 0; i < expected_count; ++i) {
+		if (expected[i] != NULL)
+			++expected_failure;
+	}
+	for (j = 0; j < actual_count; ++j) {
+		if (actual[j] != NULL)
+			++actual_failure;
+	}
+	if (expected_failure == 0 && actual_failure == 0) {
+		free(buff);
+		free(expected);
+		free(actual);
+		return (1);
+	}
+	failure_start(file, line, "File doesn't match: %s", pathname);
+	for (i = 0; i < expected_count; ++i) {
+		if (expected[i] != NULL) {
+			logprintf("  Expected but not present: %s\n", expected[i]);
+			free(expected[i]);
+		}
+	}
+	for (j = 0; j < actual_count; ++j) {
+		if (actual[j] != NULL)
+			logprintf("  Present but not expected: %s\n", actual[j]);
+	}
+	failure_finish(NULL);
+	free(buff);
+	free(expected);
+	free(actual);
+	return (0);
+}
+
 /* Test that two paths point to the same file. */
 /* As a side-effect, asserts that both files exist. */
 static int
@@ -944,8 +1163,11 @@
 	ftime.dwHighDateTime = 0;
 
 	assertion_count(file, line);
+	/* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open
+	 * a directory file. If not, CreateFile() will fail when
+	 * the pathname is a directory. */
 	h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL,
-	    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+	    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
 	if (h == INVALID_HANDLE_VALUE) {
 		failure_start(file, line, "Can't access %s\n", pathname);
 		failure_finish(NULL);
@@ -1010,14 +1232,14 @@
 		time_t now = time(NULL);
 		if (filet < now - 10 || filet > now + 1) {
 			failure_start(file, line,
-			    "File %s has %ctime %ld, %ld seconds ago\n",
+			    "File %s has %ctime %lld, %lld seconds ago\n",
 			    pathname, type, filet, now - filet);
 			failure_finish(NULL);
 			return (0);
 		}
 	} else if (filet != t || filet_nsec != nsec) {
 		failure_start(file, line,
-		    "File %s has %ctime %ld.%09ld, expected %ld.%09ld",
+		    "File %s has %ctime %lld.%09lld, expected %lld.%09lld",
 		    pathname, type, filet, filet_nsec, t, nsec);
 		failure_finish(NULL);
 		return (0);
@@ -1094,7 +1316,7 @@
 
 	assertion_count(file, line);
 	r = lstat(pathname, &st);
-	if (r == 0 && st.st_nlink == nlinks)
+	if (r == 0 && (int)st.st_nlink == nlinks)
 			return (1);
 	failure_start(file, line, "File %s has %d links, expected %d",
 	    pathname, st.st_nlink, nlinks);
@@ -1158,7 +1380,7 @@
 	/* Windows doesn't handle permissions the same way as POSIX,
 	 * so just ignore the mode tests. */
 	/* TODO: Can we do better here? */
-	if (mode >= 0 && mode != (st.st_mode & 07777)) {
+	if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) {
 		failure_start(file, line, "Dir %s has wrong mode", pathname);
 		logprintf("  Expected: 0%3o\n", mode);
 		logprintf("  Found: 0%3o\n", st.st_mode & 07777);
@@ -1191,7 +1413,7 @@
 	/* Windows doesn't handle permissions the same way as POSIX,
 	 * so just ignore the mode tests. */
 	/* TODO: Can we do better here? */
-	if (mode >= 0 && mode != (st.st_mode & 07777)) {
+	if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) {
 		failure_start(file, line, "File %s has wrong mode", pathname);
 		logprintf("  Expected: 0%3o\n", mode);
 		logprintf("  Found: 0%3o\n", st.st_mode & 07777);
@@ -1390,6 +1612,110 @@
 	return (1);
 }
 
+/* Set times, report failures. */
+int
+assertion_utimes(const char *file, int line,
+    const char *pathname, long at, long at_nsec, long mt, long mt_nsec)
+{
+	int r;
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\
+	 + (((nsec)/1000)*10))
+	HANDLE h;
+	ULARGE_INTEGER wintm;
+	FILETIME fatime, fmtime;
+	FILETIME *pat, *pmt;
+
+	assertion_count(file, line);
+	h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE,
+		    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+		    FILE_FLAG_BACKUP_SEMANTICS, NULL);
+	if (h == INVALID_HANDLE_VALUE) {
+		failure_start(file, line, "Can't access %s\n", pathname);
+		failure_finish(NULL);
+		return (0);
+	}
+
+	if (at > 0 || at_nsec > 0) {
+		wintm.QuadPart = WINTIME(at, at_nsec);
+		fatime.dwLowDateTime = wintm.LowPart;
+		fatime.dwHighDateTime = wintm.HighPart;
+		pat = &fatime;
+	} else
+		pat = NULL;
+	if (mt > 0 || mt_nsec > 0) {
+		wintm.QuadPart = WINTIME(mt, mt_nsec);
+		fmtime.dwLowDateTime = wintm.LowPart;
+		fmtime.dwHighDateTime = wintm.HighPart;
+		pmt = &fmtime;
+	} else
+		pmt = NULL;
+	if (pat != NULL || pmt != NULL)
+		r = SetFileTime(h, NULL, pat, pmt);
+	else
+		r = 1;
+	CloseHandle(h);
+	if (r == 0) {
+		failure_start(file, line, "Can't SetFileTime %s\n", pathname);
+		failure_finish(NULL);
+		return (0);
+	}
+	return (1);
+#else /* defined(_WIN32) && !defined(__CYGWIN__) */
+	struct stat st;
+	struct timeval times[2];
+
+#if !defined(__FreeBSD__)
+	mt_nsec = at_nsec = 0;	/* Generic POSIX only has whole seconds. */
+#endif
+	if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0)
+		return (1);
+
+	r = lstat(pathname, &st);
+	if (r < 0) {
+		failure_start(file, line, "Can't stat %s\n", pathname);
+		failure_finish(NULL);
+		return (0);
+	}
+
+	if (mt == 0 && mt_nsec == 0) {
+		mt = st.st_mtime;
+#if defined(__FreeBSD__)
+		mt_nsec = st.st_mtimespec.tv_nsec;
+		/* FreeBSD generally only stores to microsecond res, so round. */
+		mt_nsec = (mt_nsec / 1000) * 1000;
+#endif
+	}
+	if (at == 0 && at_nsec == 0) {
+		at = st.st_atime;
+#if defined(__FreeBSD__)
+		at_nsec = st.st_atimespec.tv_nsec;
+		/* FreeBSD generally only stores to microsecond res, so round. */
+		at_nsec = (at_nsec / 1000) * 1000;
+#endif
+	}
+
+	times[1].tv_sec = mt;
+	times[1].tv_usec = mt_nsec / 1000;
+
+	times[0].tv_sec = at;
+	times[0].tv_usec = at_nsec / 1000;
+
+#ifdef HAVE_LUTIMES
+	r = lutimes(pathname, times);
+#else
+	r = utimes(pathname, times);
+#endif
+	if (r < 0) {
+		failure_start(file, line, "Can't utimes %s\n", pathname);
+		failure_finish(NULL);
+		return (0);
+	}
+	return (1);
+#endif /* defined(_WIN32) && !defined(__CYGWIN__) */
+}
+
 /*
  *
  *  UTILITIES for use by tests.
@@ -1615,6 +1941,27 @@
 	fclose(in);
 }
 
+int
+is_LargeInode(const char *file)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	BY_HANDLE_FILE_INFORMATION bhfi;
+	int r;
+
+	r = my_GetFileInformationByName(file, &bhfi);
+	if (r != 0)
+		return (0);
+	return (bhfi.nFileIndexHigh & 0x0000FFFFUL);
+#else
+	struct stat st;
+	int64_t ino;
+
+	if (stat(file, &st) < 0)
+		return (0);
+	ino = (int64_t)st.st_ino;
+	return (ino > 0xffffffff);
+#endif
+}
 /*
  *
  * TEST management
@@ -1644,7 +1991,7 @@
  * Summarize repeated failures in the just-completed test.
  */
 static void
-test_summarize(const char *filename, int failed)
+test_summarize(int failed)
 {
 	unsigned int i;
 
@@ -1663,9 +2010,10 @@
 	for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) {
 		if (failed_lines[i].count > 1 && !failed_lines[i].skip)
 			logprintf("%s:%d: Summary: Failed %d times\n",
-			    filename, i, failed_lines[i].count);
+			    failed_filename, i, failed_lines[i].count);
 	}
 	/* Clear the failure history for the next file. */
+	failed_filename = NULL;
 	memset(failed_lines, 0, sizeof(failed_lines));
 }
 
@@ -1675,6 +2023,7 @@
 static int
 test_run(int i, const char *tmpdir)
 {
+	char workdir[1024];
 	char logfilename[64];
 	int failures_before = failures;
 	int oldumask;
@@ -1701,11 +2050,12 @@
 	logfile = fopen(logfilename, "w");
 	fprintf(logfile, "%s\n\n", tests[i].name);
 	/* Chdir() to a work dir for this specific test. */
-	if (!assertMakeDir(tests[i].name, 0755)
-	    || !assertChdir(tests[i].name)) {
+	snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name);
+	testworkdir = workdir;
+	if (!assertMakeDir(testworkdir, 0755)
+	    || !assertChdir(testworkdir)) {
 		fprintf(stderr,
-		    "ERROR: Can't chdir to work dir %s/%s\n",
-		    tmpdir, tests[i].name);
+		    "ERROR: Can't chdir to work dir %s\n", testworkdir);
 		exit(1);
 	}
 	/* Explicitly reset the locale before each test. */
@@ -1719,6 +2069,7 @@
 	/*
 	 * Clean up and report afterwards.
 	 */
+	testworkdir = NULL;
 	/* Restore umask */
 	umask(oldumask);
 	/* Reset locale. */
@@ -1731,7 +2082,7 @@
 	}
 	/* Report per-test summaries. */
 	tests[i].failures = failures - failures_before;
-	test_summarize(test_filename, tests[i].failures);
+	test_summarize(tests[i].failures);
 	/* Close the per-test log file. */
 	fclose(logfile);
 	logfile = NULL;
@@ -1791,6 +2142,7 @@
 	printf("  -q  Quiet.\n");
 	printf("  -r <dir>   Path to dir containing reference files.\n");
 	printf("      Default: Current directory.\n");
+	printf("  -u  Keep running specifies tests until one fails.\n");
 	printf("  -v  Verbose.\n");
 	printf("Available tests:\n");
 	for (i = 0; i < limit; i++)
@@ -1817,7 +2169,11 @@
 	}
 
 	/* Get the current dir. */
+#ifdef PATH_MAX
+	pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
+#else
 	pwd = getcwd(NULL, 0);
+#endif
 	while (pwd[strlen(pwd) - 1] == '\n')
 		pwd[strlen(pwd) - 1] = '\0';
 
@@ -1844,6 +2200,14 @@
 	strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
 	strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
 
+#if defined(PROGRAM_ALIAS)
+	snprintf(buff, sizeof(buff), "%s/%s/test", pwd, PROGRAM_ALIAS);
+	p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
+	if (p != NULL) goto success;
+	strncat(tried, buff, sizeof(tried) - strlen(tried) - 1);
+	strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1);
+#endif
+
 	if (memcmp(pwd, "/usr/obj", 8) == 0) {
 		snprintf(buff, sizeof(buff), "%s", pwd + 8);
 		p = slurpfile(NULL, "%s/%s", buff, KNOWNREF);
@@ -1876,16 +2240,26 @@
 main(int argc, char **argv)
 {
 	static const int limit = sizeof(tests) / sizeof(tests[0]);
-	int i, tests_run = 0, tests_failed = 0, option;
+	int i = 0, j = 0, start, end, tests_run = 0, tests_failed = 0, option;
 	time_t now;
 	char *refdir_alloc = NULL;
 	const char *progname;
+	char **saved_argv;
 	const char *tmp, *option_arg, *p;
-	char tmpdir[256];
+	char tmpdir[256], *pwd, *testprogdir, *tmp2 = NULL;
 	char tmpdir_timestamp[256];
 
 	(void)argc; /* UNUSED */
 
+	/* Get the current dir. */
+#ifdef PATH_MAX
+	pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */
+#else
+	pwd = getcwd(NULL, 0);
+#endif
+	while (pwd[strlen(pwd) - 1] == '\n')
+		pwd[strlen(pwd) - 1] = '\0';
+
 #if defined(HAVE__CrtSetReportMode)
 	/* To stop to run the default invalid parameter handler. */
 	_set_invalid_parameter_handler(invalid_parameter_handler);
@@ -1898,11 +2272,44 @@
 	 * tree.
 	 */
 	progname = p = argv[0];
+	if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL)
+	{
+		fprintf(stderr, "ERROR: Out of memory.");
+		exit(1);
+	}
+	strcpy(testprogdir, progname);
 	while (*p != '\0') {
 		/* Support \ or / dir separators for Windows compat. */
 		if (*p == '/' || *p == '\\')
+		{
 			progname = p + 1;
+			i = j;
+		}
 		++p;
+		j++;
+	}
+	testprogdir[i] = '\0';
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	if (testprogdir[0] != '/' && testprogdir[0] != '\\' &&
+	    !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') ||
+	       (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) &&
+		testprogdir[1] == ':' &&
+		(testprogdir[2] == '/' || testprogdir[2] == '\\')))
+#else
+	if (testprogdir[0] != '/')
+#endif
+	{
+		/* Fixup path for relative directories. */
+		if ((testprogdir = (char *)realloc(testprogdir,
+			strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL)
+		{
+			fprintf(stderr, "ERROR: Out of memory.");
+			exit(1);
+		}
+		memmove(testprogdir + strlen(pwd) + 1, testprogdir,
+		    strlen(testprogdir));
+		memcpy(testprogdir, pwd, strlen(pwd));
+		testprogdir[strlen(pwd)] = '/';
 	}
 
 #ifdef PROGRAM
@@ -1967,6 +2374,7 @@
 #ifdef PROGRAM
 				testprogfile = option_arg;
 #else
+				fprintf(stderr, "-p option not permitted\n");
 				usage(progname);
 #endif
 				break;
@@ -1976,10 +2384,15 @@
 			case 'r':
 				refdir = option_arg;
 				break;
+			case 'u':
+				until_failure++;
+				break;
 			case 'v':
 				verbosity++;
 				break;
 			default:
+				fprintf(stderr, "Unrecognized option '%c'\n",
+				    option);
 				usage(progname);
 			}
 		}
@@ -1990,7 +2403,19 @@
 	 */
 #ifdef PROGRAM
 	if (testprogfile == NULL)
-		usage(progname);
+	{
+		if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 +
+			strlen(PROGRAM) + 1)) == NULL)
+		{
+			fprintf(stderr, "ERROR: Out of memory.");
+			exit(1);
+		}
+		strcpy(tmp2, testprogdir);
+		strcat(tmp2, "/");
+		strcat(tmp2, PROGRAM);
+		testprogfile = tmp2;
+	}
+
 	{
 		char *testprg;
 #if defined(_WIN32) && !defined(__CYGWIN__)
@@ -2011,6 +2436,16 @@
 	}
 #endif
 
+#if !defined(_WIN32) && defined(SIGPIPE)
+	{   /* Ignore SIGPIPE signals */
+		struct sigaction sa;
+		sa.sa_handler = SIG_IGN;
+		sigemptyset(&sa.sa_mask);
+		sa.sa_flags = 0;
+		sigaction(SIGPIPE, &sa, NULL);
+	}
+#endif
+
 	/*
 	 * Create a temp directory for the following tests.
 	 * Include the time the tests started as part of the name,
@@ -2063,42 +2498,88 @@
 	/*
 	 * Run some or all of the individual tests.
 	 */
-	if (*argv == NULL) {
-		/* Default: Run all tests. */
-		for (i = 0; i < limit; i++) {
-			if (test_run(i, tmpdir))
-				tests_failed++;
-			tests_run++;
-		}
-	} else {
-		while (*(argv) != NULL) {
-			if (**argv >= '0' && **argv <= '9') {
-				i = atoi(*argv);
-				if (i < 0 || i >= limit) {
-					printf("*** INVALID Test %s\n", *argv);
-					free(refdir_alloc);
-					usage(progname);
-					/* usage() never returns */
-				}
-			} else {
-				for (i = 0; i < limit; ++i) {
-					if (strcmp(*argv, tests[i].name) == 0)
-						break;
-				}
-				if (i >= limit) {
-					printf("*** INVALID Test ``%s''\n",
-					       *argv);
-					free(refdir_alloc);
-					usage(progname);
-					/* usage() never returns */
+	saved_argv = argv;
+	do {
+		argv = saved_argv;
+		if (*argv == NULL) {
+			/* Default: Run all tests. */
+			for (i = 0; i < limit; i++) {
+				tests_run++;
+				if (test_run(i, tmpdir)) {
+					tests_failed++;
+					if (until_failure)
+						goto finish;
 				}
 			}
-			if (test_run(i, tmpdir))
-				tests_failed++;
-			tests_run++;
-			argv++;
+		} else {
+			while (*(argv) != NULL) {
+				if (**argv >= '0' && **argv <= '9') {
+					char *vp = *argv;
+					start = 0;
+					while (*vp >= '0' && *vp <= '9') {
+						start *= 10;
+						start += *vp - '0';
+						++vp;
+					}
+					if (*vp == '\0') {
+						end = start;
+					} else if (*vp == '-') {
+						++vp;
+						if (*vp == '\0') {
+							end = limit - 1;
+						} else {
+							end = 0;
+							while (*vp >= '0' && *vp <= '9') {
+								end *= 10;
+								end += *vp - '0';
+								++vp;
+							}
+						}
+					} else {
+						printf("*** INVALID Test %s\n", *argv);
+						free(refdir_alloc);
+						usage(progname);
+						return (1);
+					}
+					if (start < 0 || end >= limit || start > end) {
+						printf("*** INVALID Test %s\n", *argv);
+						free(refdir_alloc);
+						usage(progname);
+						return (1);
+					}
+				} else {
+					for (start = 0; start < limit; ++start) {
+						if (strcmp(*argv, tests[start].name) == 0)
+							break;
+					}
+					end = start;
+					if (start >= limit) {
+						printf("*** INVALID Test ``%s''\n",
+						    *argv);
+						free(refdir_alloc);
+						usage(progname);
+						/* usage() never returns */
+					}
+				}
+				while (start <= end) {
+					tests_run++;
+					if (test_run(start, tmpdir)) {
+						tests_failed++;
+						if (until_failure)
+							goto finish;
+					}
+					++start;
+				}
+				argv++;
+			}
 		}
-	}
+	} while (until_failure);
+
+finish:
+	/* Must be freed after all tests run */
+	free(tmp2);
+	free(testprogdir);
+	free(pwd);
 
 	/*
 	 * Report summary statistics.
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/test/test.h
--- a/head/contrib/libarchive/cpio/test/test.h	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/test/test.h	Fri Mar 02 16:54:40 2012 +0200
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/contrib/libarchive/cpio/test/test.h 228763 2011-12-21 11:13:29Z mm $
+ * $FreeBSD: head/contrib/libarchive/cpio/test/test.h 232153 2012-02-25 10:58:02Z mm $
  */
 
 /* Every test program should #include "test.h" as the first thing. */
@@ -48,9 +48,6 @@
 #include <sys/types.h>  /* Windows requires this before sys/stat.h */
 #include <sys/stat.h>
 
-#ifdef USE_DMALLOC
-#include <dmalloc.h>
-#endif
 #if HAVE_DIRENT_H
 #include <dirent.h>
 #endif
@@ -63,6 +60,9 @@
 #ifdef HAVE_IO_H
 #include <io.h>
 #endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -83,13 +83,9 @@
 
 /* Windows (including Visual Studio and MinGW but not Cygwin) */
 #if defined(_WIN32) && !defined(__CYGWIN__)
-#include "../cpio_windows.h"
 #if !defined(__BORLANDC__)
 #define strdup _strdup
 #endif
-#define LOCALE_DE	"deu"
-#else
-#define LOCALE_DE	"de_DE.UTF-8"
 #endif
 
 /* Visual Studio */
@@ -97,13 +93,11 @@
 #define snprintf	sprintf_s
 #endif
 
-/* Cygwin */
-#if defined(__CYGWIN__)
-/* Cygwin-1.7.x is lazy about populating nlinks, so don't
- * expect it to be accurate. */
-# define NLINKS_INACCURATE_FOR_DIRS
+#if defined(__BORLANDC__)
+#pragma warn -8068	/* Constant out of range in comparison. */
 #endif
 
+/* Haiku OS and QNX */
 #if defined(__HAIKU__) || defined(__QNXNTO__)
 /* Haiku and QNX have typedefs in stdint.h (needed for int64_t) */
 #include <stdint.h>
@@ -139,24 +133,24 @@
   assertion_equal_int(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
 /* Assert two strings are the same.  Reports value of each one if not. */
 #define assertEqualString(v1,v2)   \
-  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
+  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 0)
+#define assertEqualUTF8String(v1,v2)   \
+  assertion_equal_string(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL, 1)
 /* As above, but v1 and v2 are wchar_t * */
 #define assertEqualWString(v1,v2)   \
   assertion_equal_wstring(__FILE__, __LINE__, (v1), #v1, (v2), #v2, NULL)
 /* As above, but raw blocks of bytes. */
 #define assertEqualMem(v1, v2, l)	\
   assertion_equal_mem(__FILE__, __LINE__, (v1), #v1, (v2), #v2, (l), #l, NULL)
-/* Assert two files are the same; allow printf-style expansion of second name.
- * See below for comments about variable arguments here...
- */
-#define assertEqualFile		\
-  assertion_setup(__FILE__, __LINE__);assertion_equal_file
-/* Assert that a file is empty; supports printf-style arguments. */
-#define assertEmptyFile		\
-  assertion_setup(__FILE__, __LINE__);assertion_empty_file
-/* Assert that a file is not empty; supports printf-style arguments. */
-#define assertNonEmptyFile		\
-  assertion_setup(__FILE__, __LINE__);assertion_non_empty_file
+/* Assert two files are the same. */
+#define assertEqualFile(f1, f2)	\
+  assertion_equal_file(__FILE__, __LINE__, (f1), (f2))
+/* Assert that a file is empty. */
+#define assertEmptyFile(pathname)	\
+  assertion_empty_file(__FILE__, __LINE__, (pathname))
+/* Assert that a file is not empty. */
+#define assertNonEmptyFile(pathname)		\
+  assertion_non_empty_file(__FILE__, __LINE__, (pathname))
 #define assertFileAtime(pathname, sec, nsec)	\
   assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec)
 #define assertFileAtimeRecent(pathname)	\
@@ -166,14 +160,14 @@
 #define assertFileBirthtimeRecent(pathname) \
   assertion_file_birthtime_recent(__FILE__, __LINE__, pathname)
 /* Assert that a file exists; supports printf-style arguments. */
-#define assertFileExists		\
-  assertion_setup(__FILE__, __LINE__);assertion_file_exists
-/* Assert that a file exists; supports printf-style arguments. */
-#define assertFileNotExists		\
-  assertion_setup(__FILE__, __LINE__);assertion_file_not_exists
-/* Assert that file contents match a string; supports printf-style arguments. */
-#define assertFileContents             \
-  assertion_setup(__FILE__, __LINE__);assertion_file_contents
+#define assertFileExists(pathname) \
+  assertion_file_exists(__FILE__, __LINE__, pathname)
+/* Assert that a file exists. */
+#define assertFileNotExists(pathname) \
+  assertion_file_not_exists(__FILE__, __LINE__, pathname)
+/* Assert that file contents match a string. */
+#define assertFileContents(data, data_size, pathname) \
+  assertion_file_contents(__FILE__, __LINE__, data, data_size, pathname)
 #define assertFileMtime(pathname, sec, nsec)	\
   assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec)
 #define assertFileMtimeRecent(pathname) \
@@ -182,8 +176,10 @@
   assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks)
 #define assertFileSize(pathname, size)  \
   assertion_file_size(__FILE__, __LINE__, pathname, size)
-#define assertTextFileContents         \
-  assertion_setup(__FILE__, __LINE__);assertion_text_file_contents
+#define assertTextFileContents(text, pathname) \
+  assertion_text_file_contents(__FILE__, __LINE__, text, pathname)
+#define assertFileContainsLinesAnyOrder(pathname, lines)	\
+  assertion_file_contains_lines_any_order(__FILE__, __LINE__, pathname, lines)
 #define assertIsDir(pathname, mode)		\
   assertion_is_dir(__FILE__, __LINE__, pathname, mode)
 #define assertIsHardlink(path1, path2)	\
@@ -205,6 +201,8 @@
   assertion_make_symlink(__FILE__, __LINE__, newfile, linkto)
 #define assertUmask(mask)	\
   assertion_umask(__FILE__, __LINE__, mask)
+#define assertUtimes(pathname, atime, atime_nsec, mtime, mtime_nsec)	\
+  assertion_utimes(__FILE__, __LINE__, pathname, atime, atime_nsec, mtime, mtime_nsec)
 
 /*
  * This would be simple with C99 variadic macros, but I don't want to
@@ -213,28 +211,29 @@
  * but effective.
  */
 #define skipping	\
-  assertion_setup(__FILE__, __LINE__);test_skipping
+  skipping_setup(__FILE__, __LINE__);test_skipping
 
 /* Function declarations.  These are defined in test_utility.c. */
 void failure(const char *fmt, ...);
 int assertion_assert(const char *, int, int, const char *, void *);
 int assertion_chdir(const char *, int, const char *);
-int assertion_empty_file(const char *, ...);
-int assertion_equal_file(const char *, const char *, ...);
+int assertion_empty_file(const char *, int, const char *);
+int assertion_equal_file(const char *, int, const char *, const char *);
 int assertion_equal_int(const char *, int, long long, const char *, long long, const char *, void *);
 int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *);
-int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *);
+int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *, int);
 int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *);
 int assertion_file_atime(const char *, int, const char *, long, long);
 int assertion_file_atime_recent(const char *, int, const char *);
 int assertion_file_birthtime(const char *, int, const char *, long, long);
 int assertion_file_birthtime_recent(const char *, int, const char *);
-int assertion_file_contents(const void *, int, const char *, ...);
-int assertion_file_exists(const char *, ...);
+int assertion_file_contains_lines_any_order(const char *, int, const char *, const char **);
+int assertion_file_contents(const char *, int, const void *, int, const char *);
+int assertion_file_exists(const char *, int, const char *);
 int assertion_file_mtime(const char *, int, const char *, long, long);
 int assertion_file_mtime_recent(const char *, int, const char *);
 int assertion_file_nlinks(const char *, int, const char *, int);
-int assertion_file_not_exists(const char *, ...);
+int assertion_file_not_exists(const char *, int, const char *);
 int assertion_file_size(const char *, int, const char *, long);
 int assertion_is_dir(const char *, int, const char *, int);
 int assertion_is_hardlink(const char *, int, const char *, const char *);
@@ -245,11 +244,12 @@
 int assertion_make_file(const char *, int, const char *, int, const char *);
 int assertion_make_hardlink(const char *, int, const char *newpath, const char *);
 int assertion_make_symlink(const char *, int, const char *newpath, const char *);
-int assertion_non_empty_file(const char *, ...);
-int assertion_text_file_contents(const char *buff, const char *f);
+int assertion_non_empty_file(const char *, int, const char *);
+int assertion_text_file_contents(const char *, int, const char *buff, const char *f);
 int assertion_umask(const char *, int, int);
-void assertion_setup(const char *, int);
+int assertion_utimes(const char *, int, const char *, long, long, long, long );
 
+void skipping_setup(const char *, int);
 void test_skipping(const char *fmt, ...);
 
 /* Like sprintf, then system() */
@@ -267,6 +267,9 @@
 /* Return true if this platform can run the "gunzip" program. */
 int canGunzip(void);
 
+/* Return true if the file has large i-node number(>0xffffffff). */
+int is_LargeInode(const char *);
+
 /* Suck file into string allocated via malloc(). Call free() when done. */
 /* Supports printf-style args: slurpfile(NULL, "%s/myfile", refdir); */
 char *slurpfile(size_t *, const char *fmt, ...);
@@ -274,6 +277,9 @@
 /* Extracts named reference file to the current directory. */
 void extract_reference_file(const char *);
 
+/* Path to working directory for current test */
+const char *testworkdir;
+
 /*
  * Special interfaces for program test harness.
  */
@@ -283,3 +289,7 @@
 /* Name of exe to use in printf-formatted command strings. */
 /* On Windows, this includes leading/trailing quotes. */
 const char *testprog;
+
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/test/test_0.c
--- a/head/contrib/libarchive/cpio/test/test_0.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/test/test_0.c	Fri Mar 02 16:54:40 2012 +0200
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_0.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_0.c 232153 2012-02-25 10:58:02Z mm $");
 
 /*
  * This first test does basic sanity checks on the environment.  For
@@ -40,15 +40,23 @@
 	struct stat st;
 
 	failure("File %s does not exist?!", testprogfile);
-	if (!assertEqualInt(0, stat(testprogfile, &st)))
+	if (!assertEqualInt(0, stat(testprogfile, &st))) {
+		fprintf(stderr,
+		    "\nFile %s does not exist; aborting test.\n\n",
+		    testprog);
 		exit(1);
+	}
 
 	failure("%s is not executable?!", testprogfile);
-	if (!assert((st.st_mode & 0111) != 0))
+	if (!assert((st.st_mode & 0111) != 0)) {
+		fprintf(stderr,
+		    "\nFile %s not executable; aborting test.\n\n",
+		    testprog);
 		exit(1);
+	}
 
 	/*
-	 * Try to succesfully run the program; this requires that
+	 * Try to successfully run the program; this requires that
 	 * we know some option that will succeed.
 	 */
 	if (0 == systemf("%s --version >" DEV_NULL, testprog)) {
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/test/test_basic.c
--- a/head/contrib/libarchive/cpio/test/test_basic.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/test/test_basic.c	Fri Mar 02 16:54:40 2012 +0200
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_basic.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_basic.c 232153 2012-02-25 10:58:02Z mm $");
 
 static void
 verify_files(const char *msg)
@@ -33,12 +33,15 @@
 	 */
 
 	/* Regular file with 2 links. */
+	failure(msg);
 	assertIsReg("file", 0644);
 	failure(msg);
 	assertFileSize("file", 10);
+	failure(msg);
 	assertFileNLinks("file", 2);
 
 	/* Another name for the same file. */
+	failure(msg);
 	assertIsHardlink("linkfile", "file");
 
 	/* Symlink */
@@ -46,8 +49,11 @@
 		assertIsSymlink("symlink", "file");
 
 	/* Another file with 1 link and different permissions. */
+	failure(msg);
 	assertIsReg("file2", 0777);
+	failure(msg);
 	assertFileSize("file2", 10);
+	failure(msg);
 	assertFileNLinks("file2", 1);
 
 	/* dir */
@@ -58,7 +64,7 @@
 basic_cpio(const char *target,
     const char *pack_options,
     const char *unpack_options,
-    const char *se)
+    const char *se, const char *se2)
 {
 	int r;
 
@@ -87,7 +93,7 @@
 
 	/* Verify stderr. */
 	failure("Error invoking %s -i %s in dir %s", testprog, unpack_options, target);
-	assertTextFileContents(se, "unpack.err");
+	assertTextFileContents(se2, "unpack.err");
 
 	verify_files(pack_options);
 
@@ -125,6 +131,7 @@
 {
 	FILE *filelist;
 	const char *msg;
+	char result[1024];
 
 	assertUmask(0);
 
@@ -132,28 +139,56 @@
 	 * Create an assortment of files on disk.
 	 */
 	filelist = fopen("filelist", "w");
+	memset(result, 0, sizeof(result));
 
 	/* File with 10 bytes content. */
 	assertMakeFile("file", 0644, "1234567890");
 	fprintf(filelist, "file\n");
+	if (is_LargeInode("file"))
+		strncat(result,
+		    "bsdcpio: file: large inode number truncated: "
+		    "Numerical result out of range\n",
+		    sizeof(result) - strlen(result));
 
 	/* hardlink to above file. */
 	assertMakeHardlink("linkfile", "file");
 	fprintf(filelist, "linkfile\n");
+	if (is_LargeInode("linkfile"))
+		strncat(result,
+		    "bsdcpio: linkfile: large inode number truncated: "
+		    "Numerical result out of range\n",
+		    sizeof(result) - strlen(result));
 
 	/* Symlink to above file. */
 	if (canSymlink()) {
 		assertMakeSymlink("symlink", "file");
 		fprintf(filelist, "symlink\n");
+		if (is_LargeInode("symlink"))
+			strncat(result,
+			    "bsdcpio: symlink: large inode number truncated: "
+				"Numerical result out of range\n",
+			    sizeof(result) - strlen(result));
 	}
 
 	/* Another file with different permissions. */
 	assertMakeFile("file2", 0777, "1234567890");
 	fprintf(filelist, "file2\n");
+	if (is_LargeInode("file2"))
+		strncat(result,
+		    "bsdcpio: file2: large inode number truncated: "
+		    "Numerical result out of range\n",
+		    sizeof(result) - strlen(result));
 
 	/* Directory. */
 	assertMakeDir("dir", 0775);
 	fprintf(filelist, "dir\n");
+	if (is_LargeInode("dir"))
+		strncat(result,
+		    "bsdcpio: dir: large inode number truncated: "
+		    "Numerical result out of range\n",
+		    sizeof(result) - strlen(result));
+	strncat(result, "2 blocks\n", sizeof(result) - strlen(result));
+
 	/* All done. */
 	fclose(filelist);
 
@@ -161,12 +196,12 @@
 
 	/* Archive/dearchive with a variety of options. */
 	msg = canSymlink() ? "2 blocks\n" : "1 block\n";
-	basic_cpio("copy", "", "", msg);
-	basic_cpio("copy_odc", "--format=odc", "", msg);
-	basic_cpio("copy_newc", "-H newc", "", "2 blocks\n");
-	basic_cpio("copy_cpio", "-H odc", "", msg);
+	basic_cpio("copy", "", "", msg, msg);
+	basic_cpio("copy_odc", "--format=odc", "", msg, msg);
+	basic_cpio("copy_newc", "-H newc", "", result, "2 blocks\n");
+	basic_cpio("copy_cpio", "-H odc", "", msg, msg);
 	msg = canSymlink() ? "9 blocks\n" : "8 blocks\n";
-	basic_cpio("copy_ustar", "-H ustar", "", msg);
+	basic_cpio("copy_ustar", "-H ustar", "", msg, msg);
 
 	/* Copy in one step using -p */
 	passthrough("passthrough");
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/test/test_format_newc.c
--- a/head/contrib/libarchive/cpio/test/test_format_newc.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/test/test_format_newc.c	Fri Mar 02 16:54:40 2012 +0200
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_format_newc.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_format_newc.c 232153 2012-02-25 10:58:02Z mm $");
 
 /* Number of bytes needed to pad 'n' to multiple of 'block', assuming
  * that 'block' is a power of two. This trick can be more easily
@@ -68,6 +68,16 @@
 	return (r);
 }
 
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int
+nlinks(const char *p)
+{
+	struct stat st;
+	assertEqualInt(0, stat(p, &st));
+	return st.st_nlink;
+}
+#endif
+
 DEFINE_TEST(test_format_newc)
 {
 	FILE *list;
@@ -77,6 +87,7 @@
 	time_t t, t2, now;
 	char *p, *e;
 	size_t s, fs, ns;
+	char result[1024];
 
 	assertUmask(0);
 
@@ -111,6 +122,29 @@
 	assertMakeDir("dir", 0775);
 	fprintf(list, "dir\n");
 
+	/* Setup result message. */
+	memset(result, 0, sizeof(result));
+	if (is_LargeInode("file1"))
+		strncat(result,
+		    "bsdcpio: file1: large inode number truncated: "
+		    "Numerical result out of range\n",
+		    sizeof(result) - strlen(result) -1);
+	if (canSymlink() && is_LargeInode("symlink"))
+		strncat(result,
+		    "bsdcpio: symlink: large inode number truncated: "
+			"Numerical result out of range\n",
+		    sizeof(result) - strlen(result) -1);
+	if (is_LargeInode("dir"))
+		strncat(result,
+		    "bsdcpio: dir: large inode number truncated: "
+		    "Numerical result out of range\n",
+		    sizeof(result) - strlen(result) -1);
+	if (is_LargeInode("hardlink"))
+		strncat(result,
+		    "bsdcpio: hardlink: large inode number truncated: "
+		    "Numerical result out of range\n",
+		    sizeof(result) - strlen(result) -1);
+
 	/* Record some facts about what we just created: */
 	now = time(NULL); /* They were all created w/in last two seconds. */
 
@@ -123,10 +157,11 @@
 
 	/* Verify that nothing went to stderr. */
 	if (canSymlink()) {
-		assertTextFileContents("2 blocks\n", "newc.err");
+		strncat(result, "2 blocks\n", sizeof(result) - strlen(result));
 	} else {
-		assertTextFileContents("1 block\n", "newc.err");
+		strncat(result, "1 block\n", sizeof(result) - strlen(result));
 	}
+	assertTextFileContents(result, "newc.err");
 
 	/* Verify that stdout is a well-formed cpio file in "newc" format. */
 	p = slurpfile(&s, "newc.out");
@@ -216,10 +251,10 @@
 	/* Mode: sgid bit sometimes propagates from parent dirs, ignore it. */
 	assertEqualInt(040775, from_hex(e + 14, 8) & ~02000);
 #endif
-	assertEqualInt(from_hex(e + 22, 8), uid); /* uid */
+	assertEqualInt(uid, from_hex(e + 22, 8)); /* uid */
 	assertEqualInt(gid, from_hex(e + 30, 8)); /* gid */
-#ifndef NLINKS_INACCURATE_FOR_DIRS
-	assertEqualMem(e + 38, "00000002", 8); /* nlink */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+	assertEqualInt(nlinks("dir"), from_hex(e + 38, 8)); /* nlinks */
 #endif
 	t2 = from_hex(e + 46, 8); /* mtime */
 	failure("First entry created at t=0x%08x this entry created at t2=0x%08x", t, t2);
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/test/test_option_c.c
--- a/head/contrib/libarchive/cpio/test/test_option_c.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/test/test_option_c.c	Fri Mar 02 16:54:40 2012 +0200
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_option_c.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_option_c.c 232153 2012-02-25 10:58:02Z mm $");
 
 static int
 is_octal(const char *p, size_t l)
@@ -51,6 +51,16 @@
 	return (r);
 }
 
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int
+nlinks(const char *p)
+{
+	struct stat st;
+	assertEqualInt(0, stat(p, &st));
+	return st.st_nlink;
+}
+#endif
+
 DEFINE_TEST(test_option_c)
 {
 	FILE *filelist;
@@ -181,17 +191,19 @@
 	/* Group members bits and others bits do not work. */
 	assertEqualMem(e + 18, "040777", 6); /* Mode */
 #else
-	/* Accept 042775 to accomodate systems where sgid bit propagates. */
+	/* Accept 042775 to accommodate systems where sgid bit propagates. */
 	if (memcmp(e + 18, "042775", 6) != 0)
 		assertEqualMem(e + 18, "040775", 6); /* Mode */
 #endif
-	assertEqualInt(from_octal(e + 24, 6), uid); /* uid */
+	assertEqualInt(uid, from_octal(e + 24, 6)); /* uid */
 	/* Gid should be same as first entry. */
 	assert(is_octal(e + 30, 6)); /* gid */
 	assertEqualInt(gid, from_octal(e + 30, 6));
-#ifndef NLINKS_INACCURATE_FOR_DIRS
-	assertEqualMem(e + 36, "000002", 6); /* Nlink */
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+	assertEqualInt(nlinks("dir"), from_octal(e + 36, 6)); /* Nlink */
 #endif
+
 	t = from_octal(e + 48, 11); /* mtime */
 	assert(t <= now); /* File wasn't created in future. */
 	assert(t >= now - 2); /* File was created w/in last 2 secs. */
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/test/test_option_t.c
--- a/head/contrib/libarchive/cpio/test/test_option_t.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/test/test_option_t.c	Fri Mar 02 16:54:40 2012 +0200
@@ -23,13 +23,19 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_option_t.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_option_t.c 232153 2012-02-25 10:58:02Z mm $");
 
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
 
 DEFINE_TEST(test_option_t)
 {
 	char *p;
 	int r;
+	time_t mtime;
+	char date[32];
+	char date2[32];
 
 	/* List reference archive, make sure the TOC is correct. */
 	extract_reference_file("test_option_t.cpio");
@@ -75,17 +81,20 @@
 	/* Since -n uses numeric UID/GID, this part should be the
 	 * same on every system. */
 	assertEqualMem(p, "-rw-r--r--   1 1000     1000            0 ",42);
-	/* Date varies depending on local timezone. */
-	if (memcmp(p + 42, "Dec 31  1969", 12) == 0) {
-		/* East of Greenwich we get Dec 31, 1969. */
-	} else {
-		/* West of Greenwich get Jan 1, 1970 */
-		assertEqualMem(p + 42, "Jan ", 4);
-		/* Some systems format "Jan 01", some "Jan  1" */
-		assert(p[46] == ' ' || p[46] == '0');
-		assertEqualMem(p + 47, "1  1970 ", 8);
-	}
-	assertEqualMem(p + 54, " file", 5);
+
+	/* Date varies depending on local timezone and locale. */
+	mtime = 1;
+#ifdef HAVE_LOCALE_H
+	setlocale(LC_ALL, "");
+#endif
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	strftime(date2, sizeof(date), "%b %d  %Y", localtime(&mtime));
+	_snprintf(date, sizeof(date)-1, "%12s file", date2);
+#else
+	strftime(date2, sizeof(date), "%b %e  %Y", localtime(&mtime));
+	snprintf(date, sizeof(date)-1, "%12s file", date2);
+#endif
+	assertEqualMem(p + 42, date, strlen(date));
 	free(p);
 
 	/* But "-n" without "-t" is an error. */
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/test/test_option_u.c
--- a/head/contrib/libarchive/cpio/test/test_option_u.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/test/test_option_u.c	Fri Mar 02 16:54:40 2012 +0200
@@ -28,7 +28,7 @@
 #elif defined(HAVE_SYS_UTIME_H)
 #include <sys/utime.h>
 #endif
-__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_option_u.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_option_u.c 232153 2012-02-25 10:58:02Z mm $");
 
 DEFINE_TEST(test_option_u)
 {
@@ -41,7 +41,7 @@
 	assertMakeFile("f", 0644, "a");
 
 	/* Copy the file to the "copy" dir. */
-	r = systemf("echo f | %s -pd copy >copy.out 2>copy.err",
+	r = systemf("echo f| %s -pd copy >copy.out 2>copy.err",
 	    testprog);
 	assertEqualInt(r, 0);
 
@@ -60,7 +60,7 @@
 	assertEqualInt(0, utime("f", &times));
 
 	/* Copy the file to the "copy" dir. */
-	r = systemf("echo f | %s -pd copy >copy.out 2>copy.err",
+	r = systemf("echo f| %s -pd copy >copy.out 2>copy.err",
 	    testprog);
 	assertEqualInt(r, 0);
 
@@ -70,7 +70,7 @@
 	assertEqualMem(p, "a", 1);
 
 	/* Copy the file to the "copy" dir with -u (force) */
-	r = systemf("echo f | %s -pud copy >copy.out 2>copy.err",
+	r = systemf("echo f| %s -pud copy >copy.out 2>copy.err",
 	    testprog);
 	assertEqualInt(r, 0);
 
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/cpio/test/test_owner_parse.c
--- a/head/contrib/libarchive/cpio/test/test_owner_parse.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/cpio/test/test_owner_parse.c	Fri Mar 02 16:54:40 2012 +0200
@@ -23,16 +23,15 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "test.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_owner_parse.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/cpio/test/test_owner_parse.c 232153 2012-02-25 10:58:02Z mm $");
 
 #include "../cpio.h"
 #include "err.h"
 
 #if !defined(_WIN32)
 #define ROOT "root"
-static int root_uids[] = { 0 };
-/* Solaris 9 root has gid 1 (other) */
-static int root_gids[] = { 0, 1 };
+static const int root_uids[] = { 0 };
+static const int root_gids[] = { 0, 1 };
 #elif defined(__CYGWIN__)
 /* On cygwin, the Administrator user most likely exists (unless
  * it has been renamed or is in a non-English localization), but
@@ -43,13 +42,13 @@
  *       Use CreateWellKnownSID() and LookupAccountName()?
  */
 #define ROOT "Administrator"
-static int root_uids[] = { 500 };
-static int root_gids[] = { 513, 545, 544 };
+static const int root_uids[] = { 500 };
+static const int root_gids[] = { 513, 545, 544 };
 #endif
 
 #if defined(ROOT)
 static int
-int_in_list(int i, int *l, size_t n)
+int_in_list(int i, const int *l, size_t n)
 {
 	while (n-- > 0)
 		if (*l++ == i)
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive.h
--- a/head/contrib/libarchive/libarchive/archive.h	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive.h	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2003-2010 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -22,12 +22,16 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/contrib/libarchive/libarchive/archive.h 228773 2011-12-21 15:18:52Z mm $
+ * $FreeBSD: head/contrib/libarchive/libarchive/archive.h 232153 2012-02-25 10:58:02Z mm $
  */
 
 #ifndef ARCHIVE_H_INCLUDED
 #define	ARCHIVE_H_INCLUDED
 
+#include <sys/stat.h>
+#include <stddef.h>  /* for wchar_t */
+#include <stdio.h> /* For FILE * */
+
 /*
  * Note: archive.h is for use outside of libarchive; the configuration
  * headers (config.h, archive_platform.h, etc.) are purely internal.
@@ -36,22 +40,15 @@
  * platform macros.
  */
 #if defined(__BORLANDC__) && __BORLANDC__ >= 0x560
-# define __LA_STDINT_H <stdint.h>
-#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__)
-# define __LA_STDINT_H <inttypes.h>
+# include <stdint.h>
+#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS)
+# include <inttypes.h>
 #endif
 
-#include <sys/stat.h>
-#include <sys/types.h>  /* Linux requires this for off_t */
-#ifdef __LA_STDINT_H
-# include __LA_STDINT_H /* int64_t, etc. */
-#endif
-#include <stdio.h> /* For FILE * */
-
 /* Get appropriate definitions of standard POSIX-style types. */
 /* These should match the types used in 'struct stat' */
 #if defined(_WIN32) && !defined(__CYGWIN__)
-#define	__LA_INT64_T	__int64
+# define	__LA_INT64_T	__int64
 # if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
 #  define	__LA_SSIZE_T	ssize_t
 # elif defined(_WIN64)
@@ -67,11 +64,15 @@
 #  define	__LA_GID_T	short
 # endif
 #else
-#include <unistd.h>  /* ssize_t, uid_t, and gid_t */
-#define	__LA_INT64_T	int64_t
-#define	__LA_SSIZE_T	ssize_t
-#define	__LA_UID_T	uid_t
-#define	__LA_GID_T	gid_t
+# include <unistd.h>  /* ssize_t, uid_t, and gid_t */
+# if defined(_SCO_DS)
+#  define	__LA_INT64_T	long long
+# else
+#  define	__LA_INT64_T	int64_t
+# endif
+# define	__LA_SSIZE_T	ssize_t
+# define	__LA_UID_T	uid_t
+# define	__LA_GID_T	gid_t
 #endif
 
 /*
@@ -88,7 +89,7 @@
 #  endif
 # else
 #  ifdef __GNUC__
-#   define __LA_DECL	__attribute__((dllimport)) extern
+#   define __LA_DECL
 #  else
 #   define __LA_DECL	__declspec(dllimport)
 #  endif
@@ -98,7 +99,7 @@
 # define __LA_DECL
 #endif
 
-#if defined(__GNUC__) && __GNUC__ >= 3
+#if defined(__GNUC__) && __GNUC__ >= 3 && !defined(__MINGW32__)
 #define	__LA_PRINTF(fmtarg, firstvararg) \
 	__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
 #else
@@ -123,50 +124,18 @@
  * easy to compare versions at build time: for version a.b.c, the
  * version number is printf("%d%03d%03d",a,b,c).  For example, if you
  * know your application requires version 2.12.108 or later, you can
- * assert that ARCHIVE_VERSION >= 2012108.
- *
- * This single-number format was introduced with libarchive 1.9.0 in
- * the libarchive 1.x family and libarchive 2.2.4 in the libarchive
- * 2.x family.  The following may be useful if you really want to do
- * feature detection for earlier libarchive versions (which defined
- * ARCHIVE_API_VERSION and ARCHIVE_API_FEATURE instead):
- *
- * #ifndef ARCHIVE_VERSION_NUMBER
- * #define ARCHIVE_VERSION_NUMBER	\
- *             (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000)
- * #endif
+ * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
  */
-#define	ARCHIVE_VERSION_NUMBER 2008005
+/* Note: Compiler will complain if this does not match archive_entry.h! */
+#define	ARCHIVE_VERSION_NUMBER 3000003
 __LA_DECL int		archive_version_number(void);
 
 /*
  * Textual name/version of the library, useful for version displays.
  */
-#define	ARCHIVE_VERSION_STRING "libarchive 2.8.5"
+#define	ARCHIVE_VERSION_STRING "libarchive 3.0.3"
 __LA_DECL const char *	archive_version_string(void);
 
-#if ARCHIVE_VERSION_NUMBER < 3000000
-/*
- * Deprecated; these are older names that will be removed in favor of
- * the simpler definitions above.
- */
-#define	ARCHIVE_VERSION_STAMP	ARCHIVE_VERSION_NUMBER
-__LA_DECL int		archive_version_stamp(void);
-#define	ARCHIVE_LIBRARY_VERSION	ARCHIVE_VERSION_STRING
-__LA_DECL const char *	archive_version(void);
-#define	ARCHIVE_API_VERSION	(ARCHIVE_VERSION_NUMBER / 1000000)
-__LA_DECL int		archive_api_version(void);
-#define	ARCHIVE_API_FEATURE	((ARCHIVE_VERSION_NUMBER / 1000) % 1000)
-__LA_DECL int		archive_api_feature(void);
-#endif
-
-#if ARCHIVE_VERSION_NUMBER < 3000000
-/* This should never have been here in the first place. */
-/* Legacy of old tar assumptions, will be removed in libarchive 3.0. */
-#define	ARCHIVE_BYTES_PER_RECORD	  512
-#define	ARCHIVE_DEFAULT_BYTES_PER_BLOCK	10240
-#endif
-
 /* Declare our basic types. */
 struct archive;
 struct archive_entry;
@@ -210,48 +179,56 @@
 typedef __LA_SSIZE_T	archive_read_callback(struct archive *,
 			    void *_client_data, const void **_buffer);
 
-/* Skips at most request bytes from archive and returns the skipped amount */
-#if ARCHIVE_VERSION_NUMBER < 2000000
-/* Libarchive 1.0 used ssize_t for the return, which is only 32 bits
- * on most 32-bit platforms; not large enough. */
-typedef __LA_SSIZE_T	archive_skip_callback(struct archive *,
-			    void *_client_data, size_t request);
-#elif ARCHIVE_VERSION_NUMBER < 3000000
-/* Libarchive 2.0 used off_t here, but that is a bad idea on Linux and a
- * few other platforms where off_t varies with build settings. */
-typedef off_t		archive_skip_callback(struct archive *,
-			    void *_client_data, off_t request);
-#else
-/* Libarchive 3.0 uses int64_t here, which is actually guaranteed to be
- * 64 bits on every platform. */
+/* Skips at most request bytes from archive and returns the skipped amount.
+ * This may skip fewer bytes than requested; it may even skip zero bytes.
+ * If you do skip fewer bytes than requested, libarchive will invoke your
+ * read callback and discard data as necessary to make up the full skip.
+ */
 typedef __LA_INT64_T	archive_skip_callback(struct archive *,
 			    void *_client_data, __LA_INT64_T request);
-#endif
+
+/* Seeks to specified location in the file and returns the position.
+ * Whence values are SEEK_SET, SEEK_CUR, SEEK_END from stdio.h.
+ * Return ARCHIVE_FATAL if the seek fails for any reason.
+ */
+typedef __LA_INT64_T	archive_seek_callback(struct archive *,
+    void *_client_data, __LA_INT64_T offset, int whence);
 
 /* Returns size actually written, zero on EOF, -1 on error. */
 typedef __LA_SSIZE_T	archive_write_callback(struct archive *,
 			    void *_client_data,
 			    const void *_buffer, size_t _length);
 
-#if ARCHIVE_VERSION_NUMBER < 3000000
-/* Open callback is actually never needed; remove it in libarchive 3.0. */
 typedef int	archive_open_callback(struct archive *, void *_client_data);
-#endif
 
 typedef int	archive_close_callback(struct archive *, void *_client_data);
 
 /*
- * Codes for archive_compression.
+ * Codes to identify various stream filters.
  */
-#define	ARCHIVE_COMPRESSION_NONE	0
-#define	ARCHIVE_COMPRESSION_GZIP	1
-#define	ARCHIVE_COMPRESSION_BZIP2	2
-#define	ARCHIVE_COMPRESSION_COMPRESS	3
-#define	ARCHIVE_COMPRESSION_PROGRAM	4
-#define	ARCHIVE_COMPRESSION_LZMA	5
-#define	ARCHIVE_COMPRESSION_XZ		6
-#define	ARCHIVE_COMPRESSION_UU		7
-#define	ARCHIVE_COMPRESSION_RPM		8
+#define	ARCHIVE_FILTER_NONE	0
+#define	ARCHIVE_FILTER_GZIP	1
+#define	ARCHIVE_FILTER_BZIP2	2
+#define	ARCHIVE_FILTER_COMPRESS	3
+#define	ARCHIVE_FILTER_PROGRAM	4
+#define	ARCHIVE_FILTER_LZMA	5
+#define	ARCHIVE_FILTER_XZ	6
+#define	ARCHIVE_FILTER_UU	7
+#define	ARCHIVE_FILTER_RPM	8
+#define	ARCHIVE_FILTER_LZIP	9
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+#define	ARCHIVE_COMPRESSION_NONE	ARCHIVE_FILTER_NONE
+#define	ARCHIVE_COMPRESSION_GZIP	ARCHIVE_FILTER_GZIP
+#define	ARCHIVE_COMPRESSION_BZIP2	ARCHIVE_FILTER_BZIP2
+#define	ARCHIVE_COMPRESSION_COMPRESS	ARCHIVE_FILTER_COMPRESS
+#define	ARCHIVE_COMPRESSION_PROGRAM	ARCHIVE_FILTER_PROGRAM
+#define	ARCHIVE_COMPRESSION_LZMA	ARCHIVE_FILTER_LZMA
+#define	ARCHIVE_COMPRESSION_XZ		ARCHIVE_FILTER_XZ
+#define	ARCHIVE_COMPRESSION_UU		ARCHIVE_FILTER_UU
+#define	ARCHIVE_COMPRESSION_RPM		ARCHIVE_FILTER_RPM
+#define	ARCHIVE_COMPRESSION_LZIP	ARCHIVE_FILTER_LZIP
+#endif
 
 /*
  * Codes returned by archive_format.
@@ -265,7 +242,7 @@
  * will change the format code to indicate the extended format that
  * was used).  In other cases, it's because different tools have
  * modified the archive and so different parts of the archive
- * actually have slightly different formts.  (Both tar and cpio store
+ * actually have slightly different formats.  (Both tar and cpio store
  * format codes in each entry, so it is quite possible for each
  * entry to be in a different format.)
  */
@@ -276,6 +253,7 @@
 #define	ARCHIVE_FORMAT_CPIO_BIN_BE		(ARCHIVE_FORMAT_CPIO | 3)
 #define	ARCHIVE_FORMAT_CPIO_SVR4_NOCRC		(ARCHIVE_FORMAT_CPIO | 4)
 #define	ARCHIVE_FORMAT_CPIO_SVR4_CRC		(ARCHIVE_FORMAT_CPIO | 5)
+#define	ARCHIVE_FORMAT_CPIO_AFIO_LARGE		(ARCHIVE_FORMAT_CPIO | 6)
 #define	ARCHIVE_FORMAT_SHAR			0x20000
 #define	ARCHIVE_FORMAT_SHAR_BASE		(ARCHIVE_FORMAT_SHAR | 1)
 #define	ARCHIVE_FORMAT_SHAR_DUMP		(ARCHIVE_FORMAT_SHAR | 2)
@@ -294,6 +272,10 @@
 #define	ARCHIVE_FORMAT_MTREE			0x80000
 #define	ARCHIVE_FORMAT_RAW			0x90000
 #define	ARCHIVE_FORMAT_XAR			0xA0000
+#define	ARCHIVE_FORMAT_LHA			0xB0000
+#define	ARCHIVE_FORMAT_CAB			0xC0000
+#define	ARCHIVE_FORMAT_RAR			0xD0000
+#define	ARCHIVE_FORMAT_7ZIP			0xE0000
 
 /*-
  * Basic outline for reading an archive:
@@ -316,40 +298,81 @@
  * support_compression_bzip2().  The "all" functions provide the
  * obvious shorthand.
  */
-__LA_DECL int		 archive_read_support_compression_all(struct archive *);
-__LA_DECL int		 archive_read_support_compression_bzip2(struct archive *);
-__LA_DECL int		 archive_read_support_compression_compress(struct archive *);
-__LA_DECL int		 archive_read_support_compression_gzip(struct archive *);
-__LA_DECL int		 archive_read_support_compression_lzma(struct archive *);
-__LA_DECL int		 archive_read_support_compression_none(struct archive *);
-__LA_DECL int		 archive_read_support_compression_program(struct archive *,
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+__LA_DECL int archive_read_support_compression_all(struct archive *);
+__LA_DECL int archive_read_support_compression_bzip2(struct archive *);
+__LA_DECL int archive_read_support_compression_compress(struct archive *);
+__LA_DECL int archive_read_support_compression_gzip(struct archive *);
+__LA_DECL int archive_read_support_compression_lzip(struct archive *);
+__LA_DECL int archive_read_support_compression_lzma(struct archive *);
+__LA_DECL int archive_read_support_compression_none(struct archive *);
+__LA_DECL int archive_read_support_compression_program(struct archive *,
 		     const char *command);
-__LA_DECL int		 archive_read_support_compression_program_signature
-				(struct archive *, const char *,
+__LA_DECL int archive_read_support_compression_program_signature
+		(struct archive *, const char *,
 				    const void * /* match */, size_t);
 
-__LA_DECL int		 archive_read_support_compression_rpm(struct archive *);
-__LA_DECL int		 archive_read_support_compression_uu(struct archive *);
-__LA_DECL int		 archive_read_support_compression_xz(struct archive *);
+__LA_DECL int archive_read_support_compression_rpm(struct archive *);
+__LA_DECL int archive_read_support_compression_uu(struct archive *);
+__LA_DECL int archive_read_support_compression_xz(struct archive *);
+#endif
 
-__LA_DECL int		 archive_read_support_format_all(struct archive *);
-__LA_DECL int		 archive_read_support_format_ar(struct archive *);
-__LA_DECL int		 archive_read_support_format_cpio(struct archive *);
-__LA_DECL int		 archive_read_support_format_empty(struct archive *);
-__LA_DECL int		 archive_read_support_format_gnutar(struct archive *);
-__LA_DECL int		 archive_read_support_format_iso9660(struct archive *);
-__LA_DECL int		 archive_read_support_format_mtree(struct archive *);
-__LA_DECL int		 archive_read_support_format_raw(struct archive *);
-__LA_DECL int		 archive_read_support_format_tar(struct archive *);
-__LA_DECL int		 archive_read_support_format_xar(struct archive *);
-__LA_DECL int		 archive_read_support_format_zip(struct archive *);
+__LA_DECL int archive_read_support_filter_all(struct archive *);
+__LA_DECL int archive_read_support_filter_bzip2(struct archive *);
+__LA_DECL int archive_read_support_filter_compress(struct archive *);
+__LA_DECL int archive_read_support_filter_gzip(struct archive *);
+__LA_DECL int archive_read_support_filter_lzip(struct archive *);
+__LA_DECL int archive_read_support_filter_lzma(struct archive *);
+__LA_DECL int archive_read_support_filter_none(struct archive *);
+__LA_DECL int archive_read_support_filter_program(struct archive *,
+		     const char *command);
+__LA_DECL int archive_read_support_filter_program_signature
+		(struct archive *, const char *,
+				    const void * /* match */, size_t);
 
+__LA_DECL int archive_read_support_filter_rpm(struct archive *);
+__LA_DECL int archive_read_support_filter_uu(struct archive *);
+__LA_DECL int archive_read_support_filter_xz(struct archive *);
 
-/* Open the archive using callbacks for archive I/O. */
-__LA_DECL int		 archive_read_open(struct archive *, void *_client_data,
+__LA_DECL int archive_read_support_format_7zip(struct archive *);
+__LA_DECL int archive_read_support_format_all(struct archive *);
+__LA_DECL int archive_read_support_format_ar(struct archive *);
+__LA_DECL int archive_read_support_format_by_code(struct archive *, int);
+__LA_DECL int archive_read_support_format_cab(struct archive *);
+__LA_DECL int archive_read_support_format_cpio(struct archive *);
+__LA_DECL int archive_read_support_format_empty(struct archive *);
+__LA_DECL int archive_read_support_format_gnutar(struct archive *);
+__LA_DECL int archive_read_support_format_iso9660(struct archive *);
+__LA_DECL int archive_read_support_format_lha(struct archive *);
+__LA_DECL int archive_read_support_format_mtree(struct archive *);
+__LA_DECL int archive_read_support_format_rar(struct archive *);
+__LA_DECL int archive_read_support_format_raw(struct archive *);
+__LA_DECL int archive_read_support_format_tar(struct archive *);
+__LA_DECL int archive_read_support_format_xar(struct archive *);
+__LA_DECL int archive_read_support_format_zip(struct archive *);
+
+/* Set various callbacks. */
+__LA_DECL int archive_read_set_open_callback(struct archive *,
+    archive_open_callback *);
+__LA_DECL int archive_read_set_read_callback(struct archive *,
+    archive_read_callback *);
+__LA_DECL int archive_read_set_seek_callback(struct archive *,
+    archive_seek_callback *);
+__LA_DECL int archive_read_set_skip_callback(struct archive *,
+    archive_skip_callback *);
+__LA_DECL int archive_read_set_close_callback(struct archive *,
+    archive_close_callback *);
+/* The callback data is provided to all of the callbacks above. */
+__LA_DECL int archive_read_set_callback_data(struct archive *, void *);
+/* Opening freezes the callbacks. */
+__LA_DECL int archive_read_open1(struct archive *);
+
+/* Convenience wrappers around the above. */
+__LA_DECL int archive_read_open(struct archive *, void *_client_data,
 		     archive_open_callback *, archive_read_callback *,
 		     archive_close_callback *);
-__LA_DECL int		 archive_read_open2(struct archive *, void *_client_data,
+__LA_DECL int archive_read_open2(struct archive *, void *_client_data,
 		     archive_open_callback *, archive_read_callback *,
 		     archive_skip_callback *, archive_close_callback *);
 
@@ -359,30 +382,32 @@
  * accept a block size handle tape blocking correctly.
  */
 /* Use this if you know the filename.  Note: NULL indicates stdin. */
-__LA_DECL int		 archive_read_open_filename(struct archive *,
+__LA_DECL int archive_read_open_filename(struct archive *,
 		     const char *_filename, size_t _block_size);
+__LA_DECL int archive_read_open_filename_w(struct archive *,
+		     const wchar_t *_filename, size_t _block_size);
 /* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */
-__LA_DECL int		 archive_read_open_file(struct archive *,
+__LA_DECL int archive_read_open_file(struct archive *,
 		     const char *_filename, size_t _block_size);
 /* Read an archive that's stored in memory. */
-__LA_DECL int		 archive_read_open_memory(struct archive *,
+__LA_DECL int archive_read_open_memory(struct archive *,
 		     void * buff, size_t size);
 /* A more involved version that is only used for internal testing. */
-__LA_DECL int		archive_read_open_memory2(struct archive *a, void *buff,
+__LA_DECL int archive_read_open_memory2(struct archive *a, void *buff,
 		     size_t size, size_t read_size);
 /* Read an archive that's already open, using the file descriptor. */
-__LA_DECL int		 archive_read_open_fd(struct archive *, int _fd,
+__LA_DECL int archive_read_open_fd(struct archive *, int _fd,
 		     size_t _block_size);
 /* Read an archive that's already open, using a FILE *. */
 /* Note: DO NOT use this with tape drives. */
-__LA_DECL int		 archive_read_open_FILE(struct archive *, FILE *_file);
+__LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file);
 
 /* Parses and returns next entry header. */
-__LA_DECL int		 archive_read_next_header(struct archive *,
+__LA_DECL int archive_read_next_header(struct archive *,
 		     struct archive_entry **);
 
 /* Parses and returns next entry header using the archive_entry passed in */
-__LA_DECL int		 archive_read_next_header2(struct archive *,
+__LA_DECL int archive_read_next_header2(struct archive *,
 		     struct archive_entry *);
 
 /*
@@ -401,14 +426,8 @@
  * the desired size of the block.  The API does guarantee that offsets will
  * be strictly increasing and that returned blocks will not overlap.
  */
-#if ARCHIVE_VERSION_NUMBER < 3000000
-__LA_DECL int		 archive_read_data_block(struct archive *a,
-			    const void **buff, size_t *size, off_t *offset);
-#else
-__LA_DECL int		 archive_read_data_block(struct archive *a,
-			    const void **buff, size_t *size,
-			    __LA_INT64_T *offset);
-#endif
+__LA_DECL int archive_read_data_block(struct archive *a,
+		    const void **buff, size_t *size, __LA_INT64_T *offset);
 
 /*-
  * Some convenience functions that are built on archive_read_data:
@@ -416,23 +435,27 @@
  *  'into_buffer': writes data into memory buffer that you provide
  *  'into_fd': writes data to specified filedes
  */
-__LA_DECL int		 archive_read_data_skip(struct archive *);
-__LA_DECL int		 archive_read_data_into_buffer(struct archive *,
-			    void *buffer, __LA_SSIZE_T len);
-__LA_DECL int		 archive_read_data_into_fd(struct archive *, int fd);
+__LA_DECL int archive_read_data_skip(struct archive *);
+__LA_DECL int archive_read_data_into_fd(struct archive *, int fd);
 
 /*
  * Set read options.
  */
-/* Apply option string to the format only. */
-__LA_DECL int		archive_read_set_format_options(struct archive *_a,
-			    const char *s);
-/* Apply option string to the filter only. */
-__LA_DECL int		archive_read_set_filter_options(struct archive *_a,
-			    const char *s);
+/* Apply option to the format only. */
+__LA_DECL int archive_read_set_format_option(struct archive *_a,
+			    const char *m, const char *o,
+			    const char *v);
+/* Apply option to the filter only. */
+__LA_DECL int archive_read_set_filter_option(struct archive *_a,
+			    const char *m, const char *o,
+			    const char *v);
+/* Apply option to both the format and the filter. */
+__LA_DECL int archive_read_set_option(struct archive *_a,
+			    const char *m, const char *o,
+			    const char *v);
 /* Apply option string to both the format and the filter. */
-__LA_DECL int		archive_read_set_options(struct archive *_a,
-			    const char *s);
+__LA_DECL int archive_read_set_options(struct archive *_a,
+			    const char *opts);
 
 /*-
  * Convenience function to recreate the current entry (whose header
@@ -477,10 +500,13 @@
 #define	ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER	(0x0800)
 /* Detect blocks of 0 and write holes instead. */
 #define	ARCHIVE_EXTRACT_SPARSE			(0x1000)
+/* Default: Do not restore Mac extended metadata. */
+/* This has no effect except on Mac OS. */
+#define	ARCHIVE_EXTRACT_MAC_METADATA		(0x2000)
 
-__LA_DECL int	 archive_read_extract(struct archive *, struct archive_entry *,
+__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *,
 		     int flags);
-__LA_DECL int	 archive_read_extract2(struct archive *, struct archive_entry *,
+__LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *,
 		     struct archive * /* dest */);
 __LA_DECL void	 archive_read_extract_set_progress_callback(struct archive *,
 		     void (*_progress_func)(void *), void *_user_data);
@@ -488,7 +514,7 @@
 /* Record the dev/ino of a file that will not be written.  This is
  * generally set to the dev/ino of the archive being read. */
 __LA_DECL void		archive_read_extract_set_skip_file(struct archive *,
-		     dev_t, ino_t);
+		     __LA_INT64_T, __LA_INT64_T);
 
 /* Close the file and release most resources. */
 __LA_DECL int		 archive_read_close(struct archive *);
@@ -502,7 +528,7 @@
 
 /*-
  * To create an archive:
- *   1) Ask archive_write_new for a archive writer object.
+ *   1) Ask archive_write_new for an archive writer object.
  *   2) Set any global properties.  In particular, you should set
  *      the compression and format to use.
  *   3) Call archive_write_open to open the file (most people
@@ -516,85 +542,93 @@
  *   6) archive_write_free to cleanup the writer and release resources
  */
 __LA_DECL struct archive	*archive_write_new(void);
-__LA_DECL int		 archive_write_set_bytes_per_block(struct archive *,
+__LA_DECL int archive_write_set_bytes_per_block(struct archive *,
 		     int bytes_per_block);
-__LA_DECL int		 archive_write_get_bytes_per_block(struct archive *);
+__LA_DECL int archive_write_get_bytes_per_block(struct archive *);
 /* XXX This is badly misnamed; suggestions appreciated. XXX */
-__LA_DECL int		 archive_write_set_bytes_in_last_block(struct archive *,
+__LA_DECL int archive_write_set_bytes_in_last_block(struct archive *,
 		     int bytes_in_last_block);
-__LA_DECL int		 archive_write_get_bytes_in_last_block(struct archive *);
+__LA_DECL int archive_write_get_bytes_in_last_block(struct archive *);
 
 /* The dev/ino of a file that won't be archived.  This is used
  * to avoid recursively adding an archive to itself. */
-__LA_DECL int		 archive_write_set_skip_file(struct archive *, dev_t, ino_t);
+__LA_DECL int archive_write_set_skip_file(struct archive *,
+    __LA_INT64_T, __LA_INT64_T);
 
-__LA_DECL int		 archive_write_set_compression_bzip2(struct archive *);
-__LA_DECL int		 archive_write_set_compression_compress(struct archive *);
-__LA_DECL int		 archive_write_set_compression_gzip(struct archive *);
-__LA_DECL int		 archive_write_set_compression_lzma(struct archive *);
-__LA_DECL int		 archive_write_set_compression_none(struct archive *);
-__LA_DECL int		 archive_write_set_compression_program(struct archive *,
+#if ARCHIVE_VERSION_NUMBER < 4000000
+__LA_DECL int archive_write_set_compression_bzip2(struct archive *);
+__LA_DECL int archive_write_set_compression_compress(struct archive *);
+__LA_DECL int archive_write_set_compression_gzip(struct archive *);
+__LA_DECL int archive_write_set_compression_lzip(struct archive *);
+__LA_DECL int archive_write_set_compression_lzma(struct archive *);
+__LA_DECL int archive_write_set_compression_none(struct archive *);
+__LA_DECL int archive_write_set_compression_program(struct archive *,
 		     const char *cmd);
-__LA_DECL int		 archive_write_set_compression_xz(struct archive *);
+__LA_DECL int archive_write_set_compression_xz(struct archive *);
+#endif
+
+__LA_DECL int archive_write_add_filter_bzip2(struct archive *);
+__LA_DECL int archive_write_add_filter_compress(struct archive *);
+__LA_DECL int archive_write_add_filter_gzip(struct archive *);
+__LA_DECL int archive_write_add_filter_lzip(struct archive *);
+__LA_DECL int archive_write_add_filter_lzma(struct archive *);
+__LA_DECL int archive_write_add_filter_none(struct archive *);
+__LA_DECL int archive_write_add_filter_program(struct archive *,
+		     const char *cmd);
+__LA_DECL int archive_write_add_filter_xz(struct archive *);
+
+
 /* A convenience function to set the format based on the code or name. */
-__LA_DECL int		 archive_write_set_format(struct archive *, int format_code);
-__LA_DECL int		 archive_write_set_format_by_name(struct archive *,
+__LA_DECL int archive_write_set_format(struct archive *, int format_code);
+__LA_DECL int archive_write_set_format_by_name(struct archive *,
 		     const char *name);
 /* To minimize link pollution, use one or more of the following. */
-__LA_DECL int		 archive_write_set_format_ar_bsd(struct archive *);
-__LA_DECL int		 archive_write_set_format_ar_svr4(struct archive *);
-__LA_DECL int		 archive_write_set_format_cpio(struct archive *);
-__LA_DECL int		 archive_write_set_format_cpio_newc(struct archive *);
-__LA_DECL int		 archive_write_set_format_mtree(struct archive *);
+__LA_DECL int archive_write_set_format_7zip(struct archive *);
+__LA_DECL int archive_write_set_format_ar_bsd(struct archive *);
+__LA_DECL int archive_write_set_format_ar_svr4(struct archive *);
+__LA_DECL int archive_write_set_format_cpio(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_newc(struct archive *);
+__LA_DECL int archive_write_set_format_gnutar(struct archive *);
+__LA_DECL int archive_write_set_format_iso9660(struct archive *);
+__LA_DECL int archive_write_set_format_mtree(struct archive *);
 /* TODO: int archive_write_set_format_old_tar(struct archive *); */
-__LA_DECL int		 archive_write_set_format_pax(struct archive *);
-__LA_DECL int		 archive_write_set_format_pax_restricted(struct archive *);
-__LA_DECL int		 archive_write_set_format_shar(struct archive *);
-__LA_DECL int		 archive_write_set_format_shar_dump(struct archive *);
-__LA_DECL int		 archive_write_set_format_ustar(struct archive *);
-__LA_DECL int		 archive_write_set_format_zip(struct archive *);
-__LA_DECL int		 archive_write_open(struct archive *, void *,
+__LA_DECL int archive_write_set_format_pax(struct archive *);
+__LA_DECL int archive_write_set_format_pax_restricted(struct archive *);
+__LA_DECL int archive_write_set_format_shar(struct archive *);
+__LA_DECL int archive_write_set_format_shar_dump(struct archive *);
+__LA_DECL int archive_write_set_format_ustar(struct archive *);
+__LA_DECL int archive_write_set_format_xar(struct archive *);
+__LA_DECL int archive_write_set_format_zip(struct archive *);
+__LA_DECL int archive_write_open(struct archive *, void *,
 		     archive_open_callback *, archive_write_callback *,
 		     archive_close_callback *);
-__LA_DECL int		 archive_write_open_fd(struct archive *, int _fd);
-__LA_DECL int		 archive_write_open_filename(struct archive *, const char *_file);
+__LA_DECL int archive_write_open_fd(struct archive *, int _fd);
+__LA_DECL int archive_write_open_filename(struct archive *, const char *_file);
+__LA_DECL int archive_write_open_filename_w(struct archive *,
+		     const wchar_t *_file);
 /* A deprecated synonym for archive_write_open_filename() */
-__LA_DECL int		 archive_write_open_file(struct archive *, const char *_file);
-__LA_DECL int		 archive_write_open_FILE(struct archive *, FILE *);
+__LA_DECL int archive_write_open_file(struct archive *, const char *_file);
+__LA_DECL int archive_write_open_FILE(struct archive *, FILE *);
 /* _buffSize is the size of the buffer, _used refers to a variable that
  * will be updated after each write into the buffer. */
-__LA_DECL int		 archive_write_open_memory(struct archive *,
+__LA_DECL int archive_write_open_memory(struct archive *,
 			void *_buffer, size_t _buffSize, size_t *_used);
 
 /*
  * Note that the library will truncate writes beyond the size provided
  * to archive_write_header or pad if the provided data is short.
  */
-__LA_DECL int		 archive_write_header(struct archive *,
+__LA_DECL int archive_write_header(struct archive *,
 		     struct archive_entry *);
-#if ARCHIVE_VERSION_NUMBER < 2000000
-/* This was erroneously declared to return "int" in libarchive 1.x. */
-__LA_DECL int		 archive_write_data(struct archive *,
+__LA_DECL __LA_SSIZE_T	archive_write_data(struct archive *,
 			    const void *, size_t);
-#else
-/* Libarchive 2.0 and later return ssize_t here. */
-__LA_DECL __LA_SSIZE_T	 archive_write_data(struct archive *,
-			    const void *, size_t);
-#endif
 
-#if ARCHIVE_VERSION_NUMBER < 3000000
-/* Libarchive 1.x and 2.x use off_t for the argument, but that's not
- * stable on Linux. */
-__LA_DECL __LA_SSIZE_T	 archive_write_data_block(struct archive *,
-				    const void *, size_t, off_t);
-#else
-/* Libarchive 3.0 uses explicit int64_t to ensure consistent 64-bit support. */
+/* This interface is currently only available for archive_write_disk handles.  */
 __LA_DECL __LA_SSIZE_T	 archive_write_data_block(struct archive *,
 				    const void *, size_t, __LA_INT64_T);
-#endif
+
 __LA_DECL int		 archive_write_finish_entry(struct archive *);
 __LA_DECL int		 archive_write_close(struct archive *);
-
 /* This can fail if the archive wasn't already closed, in which case
  * archive_write_free() will implicitly call archive_write_close(). */
 __LA_DECL int		 archive_write_free(struct archive *);
@@ -606,16 +640,21 @@
 /*
  * Set write options.
  */
-/* Apply option string to the format only. */
-__LA_DECL int		archive_write_set_format_options(struct archive *_a,
-			    const char *s);
-/* Apply option string to the compressor only. */
-__LA_DECL int		archive_write_set_compressor_options(struct archive *_a,
-			    const char *s);
-/* Apply option string to both the format and the compressor. */
-__LA_DECL int		archive_write_set_options(struct archive *_a,
-			    const char *s);
-
+/* Apply option to the format only. */
+__LA_DECL int archive_write_set_format_option(struct archive *_a,
+			    const char *m, const char *o,
+			    const char *v);
+/* Apply option to the filter only. */
+__LA_DECL int archive_write_set_filter_option(struct archive *_a,
+			    const char *m, const char *o,
+			    const char *v);
+/* Apply option to both the format and the filter. */
+__LA_DECL int archive_write_set_option(struct archive *_a,
+			    const char *m, const char *o,
+			    const char *v);
+/* Apply option string to both the format and the filter. */
+__LA_DECL int archive_write_set_options(struct archive *_a,
+			    const char *opts);
 
 /*-
  * ARCHIVE_WRITE_DISK API
@@ -635,8 +674,8 @@
  */
 __LA_DECL struct archive	*archive_write_disk_new(void);
 /* This file will not be overwritten. */
-__LA_DECL int		 archive_write_disk_set_skip_file(struct archive *,
-		     dev_t, ino_t);
+__LA_DECL int archive_write_disk_set_skip_file(struct archive *,
+    __LA_INT64_T, __LA_INT64_T);
 /* Set flags to control how the next item gets created.
  * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */
 __LA_DECL int		 archive_write_disk_set_options(struct archive *,
@@ -664,14 +703,16 @@
  * your needs, you can write your own and register them.  Be sure to
  * include a cleanup function if you have allocated private data.
  */
-__LA_DECL int	 archive_write_disk_set_group_lookup(struct archive *,
-			    void * /* private_data */,
-			    __LA_GID_T (*)(void *, const char *, __LA_GID_T),
-			    void (* /* cleanup */)(void *));
-__LA_DECL int	 archive_write_disk_set_user_lookup(struct archive *,
-			    void * /* private_data */,
-			    __LA_UID_T (*)(void *, const char *, __LA_UID_T),
-			    void (* /* cleanup */)(void *));
+__LA_DECL int archive_write_disk_set_group_lookup(struct archive *,
+    void * /* private_data */,
+    __LA_INT64_T (*)(void *, const char *, __LA_INT64_T),
+    void (* /* cleanup */)(void *));
+__LA_DECL int archive_write_disk_set_user_lookup(struct archive *,
+    void * /* private_data */,
+    __LA_INT64_T (*)(void *, const char *, __LA_INT64_T),
+    void (* /* cleanup */)(void *));
+__LA_DECL __LA_INT64_T archive_write_disk_gid(struct archive *, const char *, __LA_INT64_T);
+__LA_DECL __LA_INT64_T archive_write_disk_uid(struct archive *, const char *, __LA_INT64_T);
 
 /*
  * ARCHIVE_READ_DISK API
@@ -692,32 +733,64 @@
     struct archive_entry *, int /* fd */, const struct stat *);
 /* Look up gname for gid or uname for uid. */
 /* Default implementations are very, very stupid. */
-__LA_DECL const char *archive_read_disk_gname(struct archive *, __LA_GID_T);
-__LA_DECL const char *archive_read_disk_uname(struct archive *, __LA_UID_T);
+__LA_DECL const char *archive_read_disk_gname(struct archive *, __LA_INT64_T);
+__LA_DECL const char *archive_read_disk_uname(struct archive *, __LA_INT64_T);
 /* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the
  * results for performance. */
 __LA_DECL int	archive_read_disk_set_standard_lookup(struct archive *);
 /* You can install your own lookups if you like. */
 __LA_DECL int	archive_read_disk_set_gname_lookup(struct archive *,
     void * /* private_data */,
-    const char *(* /* lookup_fn */)(void *, __LA_GID_T),
+    const char *(* /* lookup_fn */)(void *, __LA_INT64_T),
     void (* /* cleanup_fn */)(void *));
 __LA_DECL int	archive_read_disk_set_uname_lookup(struct archive *,
     void * /* private_data */,
-    const char *(* /* lookup_fn */)(void *, __LA_UID_T),
+    const char *(* /* lookup_fn */)(void *, __LA_INT64_T),
     void (* /* cleanup_fn */)(void *));
+/* Start traversal. */
+__LA_DECL int	archive_read_disk_open(struct archive *, const char *);
+__LA_DECL int	archive_read_disk_open_w(struct archive *, const wchar_t *);
+/*
+ * Request that current entry be visited.  If you invoke it on every
+ * directory, you'll get a physical traversal.  This is ignored if the
+ * current entry isn't a directory or a link to a directory.  So, if
+ * you invoke this on every returned path, you'll get a full logical
+ * traversal.
+ */
+__LA_DECL int	archive_read_disk_descend(struct archive *);
+__LA_DECL int	archive_read_disk_current_filesystem(struct archive *);
+__LA_DECL int	archive_read_disk_current_filesystem_is_synthetic(struct archive *);
+__LA_DECL int	archive_read_disk_current_filesystem_is_remote(struct archive *);
+/* Request that the access time of the entry visited by travesal be restored. */
+__LA_DECL int  archive_read_disk_set_atime_restored(struct archive *);
 
 /*
  * Accessor functions to read/set various information in
  * the struct archive object:
  */
-/* Bytes written after compression or read before decompression. */
+
+/* Number of filters in the current filter pipeline. */
+/* Filter #0 is the one closest to the format, -1 is a synonym for the
+ * last filter, which is always the pseudo-filter that wraps the
+ * client callbacks. */
+__LA_DECL int		 archive_filter_count(struct archive *);
+__LA_DECL __LA_INT64_T	 archive_filter_bytes(struct archive *, int);
+__LA_DECL int		 archive_filter_code(struct archive *, int);
+__LA_DECL const char *	 archive_filter_name(struct archive *, int);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* These don't properly handle multiple filters, so are deprecated and
+ * will eventually be removed. */
+/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, -1); */
 __LA_DECL __LA_INT64_T	 archive_position_compressed(struct archive *);
-/* Bytes written to compressor or read from decompressor. */
+/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, 0); */
 __LA_DECL __LA_INT64_T	 archive_position_uncompressed(struct archive *);
+/* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */
+__LA_DECL const char	*archive_compression_name(struct archive *);
+/* As of libarchive 3.0, this is an alias for archive_filter_code(a, 0); */
+__LA_DECL int		 archive_compression(struct archive *);
+#endif
 
-__LA_DECL const char	*archive_compression_name(struct archive *);
-__LA_DECL int		 archive_compression(struct archive *);
 __LA_DECL int		 archive_errno(struct archive *);
 __LA_DECL const char	*archive_error_string(struct archive *);
 __LA_DECL const char	*archive_format_name(struct archive *);
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_check_magic.c
--- a/head/contrib/libarchive/libarchive/archive_check_magic.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_check_magic.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2003-2010 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_check_magic.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_check_magic.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -86,49 +86,89 @@
 	}
 }
 
+static const char *
+archive_handle_type_name(unsigned m)
+{
+	switch (m) {
+	case ARCHIVE_WRITE_MAGIC:	return ("archive_write");
+	case ARCHIVE_READ_MAGIC:	return ("archive_read");
+	case ARCHIVE_WRITE_DISK_MAGIC:	return ("archive_write_disk");
+	case ARCHIVE_READ_DISK_MAGIC:	return ("archive_read_disk");
+	default:			return NULL;
+	}
+}
 
-static void
-write_all_states(unsigned int states)
+
+static char *
+write_all_states(char *buff, unsigned int states)
 {
 	unsigned int lowbit;
 
+	buff[0] = '\0';
+
 	/* A trick for computing the lowest set bit. */
 	while ((lowbit = states & (1 + ~states)) != 0) {
 		states &= ~lowbit;		/* Clear the low bit. */
-		errmsg(state_name(lowbit));
+		strcat(buff, state_name(lowbit));
 		if (states != 0)
-			errmsg("/");
+			strcat(buff, "/");
 	}
+	return buff;
 }
 
 /*
- * Check magic value and current state; bail if it isn't valid.
+ * Check magic value and current state.
+ *   Magic value mismatches are fatal and result in calls to abort().
+ *   State mismatches return ARCHIVE_FATAL.
+ *   Otherwise, returns ARCHIVE_OK.
  *
  * This is designed to catch serious programming errors that violate
  * the libarchive API.
  */
-void
+int
 __archive_check_magic(struct archive *a, unsigned int magic,
     unsigned int state, const char *function)
 {
-	if (a->magic != magic) {
-		errmsg("INTERNAL ERROR: Function ");
+	char states1[64];
+	char states2[64];
+	const char *handle_type;
+
+	/*
+	 * If this isn't some form of archive handle,
+	 * then the library user has screwed up so bad that
+	 * we don't even have a reliable way to report an error.
+	 */
+	handle_type = archive_handle_type_name(a->magic);
+
+	if (!handle_type) {
+		errmsg("PROGRAMMER ERROR: Function ");
 		errmsg(function);
-		errmsg(" invoked with invalid struct archive structure.\n");
+		errmsg(" invoked with invalid archive handle.\n");
 		diediedie();
 	}
 
-	if (state == ARCHIVE_STATE_ANY)
-		return;
+	if (a->magic != magic) {
+		archive_set_error(a, -1,
+		    "PROGRAMMER ERROR: Function '%s' invoked"
+		    " on '%s' archive object, which is not supported.",
+		    function,
+		    handle_type);
+		a->state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
 
 	if ((a->state & state) == 0) {
-		errmsg("INTERNAL ERROR: Function '");
-		errmsg(function);
-		errmsg("' invoked with archive structure in state '");
-		write_all_states(a->state);
-		errmsg("', should be in state '");
-		write_all_states(state);
-		errmsg("'\n");
-		diediedie();
+		/* If we're already FATAL, don't overwrite the error. */
+		if (a->state != ARCHIVE_STATE_FATAL)
+			archive_set_error(a, -1,
+			    "INTERNAL ERROR: Function '%s' invoked with"
+			    " archive structure in state '%s',"
+			    " should be in state '%s'",
+			    function,
+			    write_all_states(states1, a->state),
+			    write_all_states(states2, state));
+		a->state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
 	}
+	return ARCHIVE_OK;
 }
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_crc32.h
--- a/head/contrib/libarchive/libarchive/archive_crc32.h	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_crc32.h	Fri Mar 02 16:54:40 2012 +0200
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/contrib/libarchive/libarchive/archive_crc32.h 228763 2011-12-21 11:13:29Z mm $
+ * $FreeBSD: head/contrib/libarchive/libarchive/archive_crc32.h 232153 2012-02-25 10:58:02Z mm $
  */
 
 #ifndef __LIBARCHIVE_BUILD
@@ -60,6 +60,18 @@
 	}
 
 	crc = crc ^ 0xffffffffUL;
+	/* A use of this loop is about 20% - 30% faster than
+	 * no use version in any optimization option of gcc.  */
+	for (;len >= 8; len -= 8) {
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+	}
 	while (len--)
 		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
 	return (crc ^ 0xffffffffUL);
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_entry.3
--- a/head/contrib/libarchive/libarchive/archive_entry.3	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_entry.3	Fri Mar 02 16:54:40 2012 +0200
@@ -1,4 +1,5 @@
 .\" Copyright (c) 2003-2007 Tim Kientzle
+.\" Copyright (c) 2010 Joerg Sonnenberger
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -22,269 +23,27 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: head/contrib/libarchive/libarchive/archive_entry.3 228773 2011-12-21 15:18:52Z mm $
+.\" $FreeBSD: head/contrib/libarchive/libarchive/archive_entry.3 232153 2012-02-25 10:58:02Z mm $
 .\"
-.Dd May 12, 2008
+.Dd Feburary 22, 2010
 .Dt ARCHIVE_ENTRY 3
 .Os
 .Sh NAME
-.Nm archive_entry_acl_add_entry ,
-.Nm archive_entry_acl_add_entry_w ,
-.Nm archive_entry_acl_clear ,
-.Nm archive_entry_acl_count ,
-.Nm archive_entry_acl_next ,
-.Nm archive_entry_acl_next_w ,
-.Nm archive_entry_acl_reset ,
-.Nm archive_entry_acl_text_w ,
-.Nm archive_entry_atime ,
-.Nm archive_entry_atime_nsec ,
 .Nm archive_entry_clear ,
 .Nm archive_entry_clone ,
-.Nm archive_entry_copy_fflags_text ,
-.Nm archive_entry_copy_fflags_text_w ,
-.Nm archive_entry_copy_gname ,
-.Nm archive_entry_copy_gname_w ,
-.Nm archive_entry_copy_hardlink ,
-.Nm archive_entry_copy_hardlink_w ,
-.Nm archive_entry_copy_link ,
-.Nm archive_entry_copy_link_w ,
-.Nm archive_entry_copy_pathname_w ,
-.Nm archive_entry_copy_sourcepath ,
-.Nm archive_entry_copy_stat ,
-.Nm archive_entry_copy_symlink ,
-.Nm archive_entry_copy_symlink_w ,
-.Nm archive_entry_copy_uname ,
-.Nm archive_entry_copy_uname_w ,
-.Nm archive_entry_dev ,
-.Nm archive_entry_devmajor ,
-.Nm archive_entry_devminor ,
-.Nm archive_entry_filetype ,
-.Nm archive_entry_fflags ,
-.Nm archive_entry_fflags_text ,
 .Nm archive_entry_free ,
-.Nm archive_entry_gid ,
-.Nm archive_entry_gname ,
-.Nm archive_entry_hardlink ,
-.Nm archive_entry_ino ,
-.Nm archive_entry_mode ,
-.Nm archive_entry_mtime ,
-.Nm archive_entry_mtime_nsec ,
-.Nm archive_entry_nlink ,
 .Nm archive_entry_new ,
-.Nm archive_entry_pathname ,
-.Nm archive_entry_pathname_w ,
-.Nm archive_entry_rdev ,
-.Nm archive_entry_rdevmajor ,
-.Nm archive_entry_rdevminor ,
-.Nm archive_entry_set_atime ,
-.Nm archive_entry_set_ctime ,
-.Nm archive_entry_set_dev ,
-.Nm archive_entry_set_devmajor ,
-.Nm archive_entry_set_devminor ,
-.Nm archive_entry_set_filetype ,
-.Nm archive_entry_set_fflags ,
-.Nm archive_entry_set_gid ,
-.Nm archive_entry_set_gname ,
-.Nm archive_entry_set_hardlink ,
-.Nm archive_entry_set_link ,
-.Nm archive_entry_set_mode ,
-.Nm archive_entry_set_mtime ,
-.Nm archive_entry_set_pathname ,
-.Nm archive_entry_set_rdevmajor ,
-.Nm archive_entry_set_rdevminor ,
-.Nm archive_entry_set_size ,
-.Nm archive_entry_set_symlink ,
-.Nm archive_entry_set_uid ,
-.Nm archive_entry_set_uname ,
-.Nm archive_entry_size ,
-.Nm archive_entry_sourcepath ,
-.Nm archive_entry_stat ,
-.Nm archive_entry_symlink ,
-.Nm archive_entry_uid ,
-.Nm archive_entry_uname
-.Nd functions for manipulating archive entry descriptions
+.Nd functions for managing archive entry descriptions
 .Sh SYNOPSIS
 .In archive_entry.h
-.Ft void
-.Fo archive_entry_acl_add_entry
-.Fa "struct archive_entry *"
-.Fa "int type"
-.Fa "int permset"
-.Fa "int tag"
-.Fa "int qual"
-.Fa "const char *name"
-.Fc
-.Ft void
-.Fo archive_entry_acl_add_entry_w
-.Fa "struct archive_entry *"
-.Fa "int type"
-.Fa "int permset"
-.Fa "int tag"
-.Fa "int qual"
-.Fa "const wchar_t *name"
-.Fc
-.Ft void
-.Fn archive_entry_acl_clear "struct archive_entry *"
-.Ft int
-.Fn archive_entry_acl_count "struct archive_entry *" "int type"
-.Ft int
-.Fo archive_entry_acl_next
-.Fa "struct archive_entry *"
-.Fa "int want_type"
-.Fa "int *type"
-.Fa "int *permset"
-.Fa "int *tag"
-.Fa "int *qual"
-.Fa "const char **name"
-.Fc
-.Ft int
-.Fo archive_entry_acl_next_w
-.Fa "struct archive_entry *"
-.Fa "int want_type"
-.Fa "int *type"
-.Fa "int *permset"
-.Fa "int *tag"
-.Fa "int *qual"
-.Fa "const wchar_t **name"
-.Fc
-.Ft int
-.Fn archive_entry_acl_reset "struct archive_entry *" "int want_type"
-.Ft const wchar_t *
-.Fn archive_entry_acl_text_w "struct archive_entry *" "int flags"
-.Ft time_t
-.Fn archive_entry_atime "struct archive_entry *"
-.Ft long
-.Fn archive_entry_atime_nsec "struct archive_entry *"
 .Ft "struct archive_entry *"
 .Fn archive_entry_clear "struct archive_entry *"
 .Ft struct archive_entry *
 .Fn archive_entry_clone "struct archive_entry *"
-.Ft const char * *
-.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const char *"
-.Ft const wchar_t *
-.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const wchar_t *"
-.Ft void
-.Fn archive_entry_copy_gname "struct archive_entry *" "const char *"
-.Ft void
-.Fn archive_entry_copy_gname_w "struct archive_entry *" "const wchar_t *"
-.Ft void
-.Fn archive_entry_copy_hardlink "struct archive_entry *" "const char *"
-.Ft void
-.Fn archive_entry_copy_hardlink_w "struct archive_entry *" "const wchar_t *"
-.Ft void
-.Fn archive_entry_copy_sourcepath "struct archive_entry *" "const char *"
-.Ft void
-.Fn archive_entry_copy_pathname_w "struct archive_entry *" "const wchar_t *"
-.Ft void
-.Fn archive_entry_copy_stat "struct archive_entry *" "const struct stat *"
-.Ft void
-.Fn archive_entry_copy_symlink "struct archive_entry *" "const char *"
-.Ft void
-.Fn archive_entry_copy_symlink_w "struct archive_entry *" "const wchar_t *"
-.Ft void
-.Fn archive_entry_copy_uname "struct archive_entry *" "const char *"
-.Ft void
-.Fn archive_entry_copy_uname_w "struct archive_entry *" "const wchar_t *"
-.Ft dev_t
-.Fn archive_entry_dev "struct archive_entry *"
-.Ft dev_t
-.Fn archive_entry_devmajor "struct archive_entry *"
-.Ft dev_t
-.Fn archive_entry_devminor "struct archive_entry *"
-.Ft mode_t
-.Fn archive_entry_filetype "struct archive_entry *"
-.Ft void
-.Fo archive_entry_fflags
-.Fa "struct archive_entry *"
-.Fa "unsigned long *set"
-.Fa "unsigned long *clear"
-.Fc
-.Ft const char *
-.Fn archive_entry_fflags_text "struct archive_entry *"
 .Ft void
 .Fn archive_entry_free "struct archive_entry *"
-.Ft const char *
-.Fn archive_entry_gname "struct archive_entry *"
-.Ft const char *
-.Fn archive_entry_hardlink "struct archive_entry *"
-.Ft ino_t
-.Fn archive_entry_ino "struct archive_entry *"
-.Ft mode_t
-.Fn archive_entry_mode "struct archive_entry *"
-.Ft time_t
-.Fn archive_entry_mtime "struct archive_entry *"
-.Ft long
-.Fn archive_entry_mtime_nsec "struct archive_entry *"
-.Ft unsigned int
-.Fn archive_entry_nlink "struct archive_entry *"
 .Ft struct archive_entry *
 .Fn archive_entry_new "void"
-.Ft const char *
-.Fn archive_entry_pathname "struct archive_entry *"
-.Ft const wchar_t *
-.Fn archive_entry_pathname_w "struct archive_entry *"
-.Ft dev_t
-.Fn archive_entry_rdev "struct archive_entry *"
-.Ft dev_t
-.Fn archive_entry_rdevmajor "struct archive_entry *"
-.Ft dev_t
-.Fn archive_entry_rdevminor "struct archive_entry *"
-.Ft void
-.Fn archive_entry_set_dev "struct archive_entry *" "dev_t"
-.Ft void
-.Fn archive_entry_set_devmajor "struct archive_entry *" "dev_t"
-.Ft void
-.Fn archive_entry_set_devminor "struct archive_entry *" "dev_t"
-.Ft void
-.Fn archive_entry_set_filetype "struct archive_entry *" "unsigned int"
-.Ft void
-.Fo archive_entry_set_fflags
-.Fa "struct archive_entry *"
-.Fa "unsigned long set"
-.Fa "unsigned long clear"
-.Fc
-.Ft void
-.Fn archive_entry_set_gid "struct archive_entry *" "gid_t"
-.Ft void
-.Fn archive_entry_set_gname "struct archive_entry *" "const char *"
-.Ft void
-.Fn archive_entry_set_hardlink "struct archive_entry *" "const char *"
-.Ft void
-.Fn archive_entry_set_ino "struct archive_entry *" "unsigned long"
-.Ft void
-.Fn archive_entry_set_link "struct archive_entry *" "const char *"
-.Ft void
-.Fn archive_entry_set_mode "struct archive_entry *" "mode_t"
-.Ft void
-.Fn archive_entry_set_mtime "struct archive_entry *" "time_t" "long nanos"
-.Ft void
-.Fn archive_entry_set_nlink "struct archive_entry *" "unsigned int"
-.Ft void
-.Fn archive_entry_set_pathname "struct archive_entry *" "const char *"
-.Ft void
-.Fn archive_entry_set_rdev "struct archive_entry *" "dev_t"
-.Ft void
-.Fn archive_entry_set_rdevmajor "struct archive_entry *" "dev_t"
-.Ft void
-.Fn archive_entry_set_rdevminor "struct archive_entry *" "dev_t"
-.Ft void
-.Fn archive_entry_set_size "struct archive_entry *" "int64_t"
-.Ft void
-.Fn archive_entry_set_symlink "struct archive_entry *" "const char *"
-.Ft void
-.Fn archive_entry_set_uid "struct archive_entry *" "uid_t"
-.Ft void
-.Fn archive_entry_set_uname "struct archive_entry *" "const char *"
-.Ft int64_t
-.Fn archive_entry_size "struct archive_entry *"
-.Ft const char *
-.Fn archive_entry_sourcepath "struct archive_entry *"
-.Ft const struct stat *
-.Fn archive_entry_stat "struct archive_entry *"
-.Ft const char *
-.Fn archive_entry_symlink "struct archive_entry *"
-.Ft const char *
-.Fn archive_entry_uname "struct archive_entry *"
 .Sh DESCRIPTION
 These functions create and manipulate data objects that
 represent entries within an archive.
@@ -320,8 +79,24 @@
 .Tn struct archive_entry
 object.
 .El
-.Ss Set and Get Functions
-Most of the functions here set or read entries in an object.
+.Ss Function groups
+Due to high number of functions, the accessor functions can be found in
+man pages grouped by the purpose.
+.Bl -tag -width ".Xr archive_entry_perms 3"
+.It Xr archive_entry_acl 3
+Access Control List manipulation
+.It Xr archive_entry_paths 3
+Path name manipulation
+.It Xr archive_entry_perms 3
+User, group and mode manipulation
+.It Xr archive_entry_stat 3
+Functions not in the other groups and copying to/from
+.Vt struct stat .
+.It Xr archive_entry_time 3
+Time field manipulation
+.El
+.Pp
+Most of the functions set or read entries in an object.
 Such functions have one of the following forms:
 .Bl -tag -compact -width indent
 .It Fn archive_entry_set_XXXX
@@ -350,75 +125,15 @@
 narrow string for the same data, the previously-set wide string will
 be discarded in favor of the new data.
 .Pp
-There are a few set/get functions that merit additional description:
-.Bl -tag -compact -width indent
-.It Fn archive_entry_set_link
-This function sets the symlink field if it is already set.
-Otherwise, it sets the hardlink field.
-.El
-.Ss File Flags
-File flags are transparently converted between a bitmap
-representation and a textual format.
-For example, if you set the bitmap and ask for text, the library
-will build a canonical text format.
-However, if you set a text format and request a text format,
-you will get back the same text, even if it is ill-formed.
-If you need to canonicalize a textual flags string, you should first set the
-text form, then request the bitmap form, then use that to set the bitmap form.
-Setting the bitmap format will clear the internal text representation
-and force it to be reconstructed when you next request the text form.
-.Pp
-The bitmap format consists of two integers, one containing bits
-that should be set, the other specifying bits that should be
-cleared.
-Bits not mentioned in either bitmap will be ignored.
-Usually, the bitmap of bits to be cleared will be set to zero.
-In unusual circumstances, you can force a fully-specified set
-of file flags by setting the bitmap of flags to clear to the complement
-of the bitmap of flags to set.
-(This differs from
-.Xr fflagstostr 3 ,
-which only includes names for set bits.)
-Converting a bitmap to a textual string is a platform-specific
-operation; bits that are not meaningful on the current platform
-will be ignored.
-.Pp
-The canonical text format is a comma-separated list of flag names.
-The
-.Fn archive_entry_copy_fflags_text
-and
-.Fn archive_entry_copy_fflags_text_w
-functions parse the provided text and sets the internal bitmap values.
-This is a platform-specific operation; names that are not meaningful
-on the current platform will be ignored.
-The function returns a pointer to the start of the first name that was not
-recognized, or NULL if every name was recognized.
-Note that every name--including names that follow an unrecognized name--will
-be evaluated, and the bitmaps will be set to reflect every name that is
-recognized.
-(In particular, this differs from
-.Xr strtofflags 3 ,
-which stops parsing at the first unrecognized name.)
-.Ss ACL Handling
-XXX This needs serious help.
-XXX
-.Pp
-An
-.Dq Access Control List
-(ACL) is a list of permissions that grant access to particular users or
-groups beyond what would normally be provided by standard POSIX mode bits.
-The ACL handling here addresses some deficiencies in the POSIX.1e draft 17 ACL
-specification.
-In particular, POSIX.1e draft 17 specifies several different formats, but
-none of those formats include both textual user/group names and numeric
-UIDs/GIDs.
-.Pp
-XXX explain ACL stuff XXX
 .\" .Sh EXAMPLE
 .\" .Sh RETURN VALUES
 .\" .Sh ERRORS
 .Sh SEE ALSO
-.Xr archive 3
+.Xr archive 3 ,
+.Xr archive_entry_acl 3 ,
+.Xr archive_entry_paths 3 ,
+.Xr archive_entry_perms 3 ,
+.Xr archive_entry_time 3
 .Sh HISTORY
 The
 .Nm libarchive
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_entry.c
--- a/head/contrib/libarchive/libarchive/archive_entry.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_entry.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_entry.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_entry.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -39,6 +39,9 @@
 #include <sys/sysmacros.h>
 #define HAVE_MAJOR
 #endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
 #ifdef HAVE_LIMITS_H
 #include <limits.h>
 #endif
@@ -68,13 +71,12 @@
 #endif
 
 #include "archive.h"
+#include "archive_acl_private.h"
 #include "archive_entry.h"
+#include "archive_entry_locale.h"
 #include "archive_private.h"
 #include "archive_entry_private.h"
 
-#undef max
-#define	max(a, b)	((a)>(b)?(a):(b))
-
 #if !defined(HAVE_MAJOR) && !defined(major)
 /* Replacement for major/minor/makedev. */
 #define	major(x) ((int)(0x00ff & ((x) >> 8)))
@@ -98,39 +100,26 @@
 #define ae_makedev(maj, min) makedev((maj), (min))
 #endif
 
-static void	aes_clean(struct aes *);
-static void	aes_copy(struct aes *dest, struct aes *src);
-static const char *	aes_get_mbs(struct aes *);
-static const wchar_t *	aes_get_wcs(struct aes *);
-static int	aes_set_mbs(struct aes *, const char *mbs);
-static int	aes_copy_mbs(struct aes *, const char *mbs);
-/* static void	aes_set_wcs(struct aes *, const wchar_t *wcs); */
-static int	aes_copy_wcs(struct aes *, const wchar_t *wcs);
-static int	aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t);
+/*
+ * This adjustment is needed to support the following idiom for adding
+ * 1000ns to the stored time:
+ * archive_entry_set_atime(archive_entry_atime(),
+ *                         archive_entry_atime_nsec() + 1000)
+ * The additional if() here compensates for ambiguity in the C standard,
+ * which permits two possible interpretations of a % b when a is negative.
+ */
+#define FIX_NS(t,ns) \
+	do {	\
+		t += ns / 1000000000; \
+		ns %= 1000000000; \
+		if (ns < 0) { --t; ns += 1000000000; } \
+	} while (0)
 
 static char *	 ae_fflagstostr(unsigned long bitset, unsigned long bitclear);
 static const wchar_t	*ae_wcstofflags(const wchar_t *stringp,
 		    unsigned long *setp, unsigned long *clrp);
 static const char	*ae_strtofflags(const char *stringp,
 		    unsigned long *setp, unsigned long *clrp);
-static void	append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
-		    const wchar_t *wname, int perm, int id);
-static void	append_id_w(wchar_t **wp, int id);
-
-static int	acl_special(struct archive_entry *entry,
-		    int type, int permset, int tag);
-static struct ae_acl *acl_new_entry(struct archive_entry *entry,
-		    int type, int permset, int tag, int id);
-static int	isint_w(const wchar_t *start, const wchar_t *end, int *result);
-static int	ismode_w(const wchar_t *start, const wchar_t *end, int *result);
-static void	next_field_w(const wchar_t **wp, const wchar_t **start,
-		    const wchar_t **end, wchar_t *sep);
-static int	prefix_w(const wchar_t *start, const wchar_t *end,
-		    const wchar_t *test);
-static void
-archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type,
-		    int permset, int tag, int id, const wchar_t *name, size_t);
-
 
 #ifndef HAVE_WCSCPY
 static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
@@ -154,214 +143,6 @@
 /* Good enough for simple equality testing, but not for sorting. */
 #define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
 #endif
-#ifndef HAVE_WMEMCPY
-#define wmemcpy(a,b,i)  (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
-#endif
-
-static void
-aes_clean(struct aes *aes)
-{
-	if (aes->aes_wcs) {
-		free((wchar_t *)(uintptr_t)aes->aes_wcs);
-		aes->aes_wcs = NULL;
-	}
-	archive_string_free(&(aes->aes_mbs));
-	archive_string_free(&(aes->aes_utf8));
-	aes->aes_set = 0;
-}
-
-static void
-aes_copy(struct aes *dest, struct aes *src)
-{
-	wchar_t *wp;
-
-	dest->aes_set = src->aes_set;
-	archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs));
-	archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8));
-
-	if (src->aes_wcs != NULL) {
-		wp = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1)
-		    * sizeof(wchar_t));
-		if (wp == NULL)
-			__archive_errx(1, "No memory for aes_copy()");
-		wcscpy(wp, src->aes_wcs);
-		dest->aes_wcs = wp;
-	}
-}
-
-static const char *
-aes_get_utf8(struct aes *aes)
-{
-	if (aes->aes_set & AES_SET_UTF8)
-		return (aes->aes_utf8.s);
-	if ((aes->aes_set & AES_SET_WCS)
-	    && archive_strappend_w_utf8(&(aes->aes_utf8), aes->aes_wcs) != NULL) {
-		aes->aes_set |= AES_SET_UTF8;
-		return (aes->aes_utf8.s);
-	}
-	return (NULL);
-}
-
-static const char *
-aes_get_mbs(struct aes *aes)
-{
-	/* If we already have an MBS form, return that immediately. */
-	if (aes->aes_set & AES_SET_MBS)
-		return (aes->aes_mbs.s);
-	/* If there's a WCS form, try converting with the native locale. */
-	if ((aes->aes_set & AES_SET_WCS)
-	    && archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) != NULL) {
-		aes->aes_set |= AES_SET_MBS;
-		return (aes->aes_mbs.s);
-	}
-	/* We'll use UTF-8 for MBS if all else fails. */
-	return (aes_get_utf8(aes));
-}
-
-static const wchar_t *
-aes_get_wcs(struct aes *aes)
-{
-	wchar_t *w;
-	size_t r;
-
-	/* Return WCS form if we already have it. */
-	if (aes->aes_set & AES_SET_WCS)
-		return (aes->aes_wcs);
-
-	if (aes->aes_set & AES_SET_MBS) {
-		/* Try converting MBS to WCS using native locale. */
-		/*
-		 * No single byte will be more than one wide character,
-		 * so this length estimate will always be big enough.
-		 */
-		size_t wcs_length = aes->aes_mbs.length;
-
-		w = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t));
-		if (w == NULL)
-			__archive_errx(1, "No memory for aes_get_wcs()");
-		r = mbstowcs(w, aes->aes_mbs.s, wcs_length);
-		if (r != (size_t)-1 && r != 0) {
-			w[r] = 0;
-			aes->aes_set |= AES_SET_WCS;
-			return (aes->aes_wcs = w);
-		}
-		free(w);
-	}
-
-	if (aes->aes_set & AES_SET_UTF8) {
-		/* Try converting UTF8 to WCS. */
-		aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8));
-		if (aes->aes_wcs != NULL)
-			aes->aes_set |= AES_SET_WCS;
-		return (aes->aes_wcs);
-	}
-	return (NULL);
-}
-
-static int
-aes_set_mbs(struct aes *aes, const char *mbs)
-{
-	return (aes_copy_mbs(aes, mbs));
-}
-
-static int
-aes_copy_mbs(struct aes *aes, const char *mbs)
-{
-	if (mbs == NULL) {
-		aes->aes_set = 0;
-		return (0);
-	}
-	aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
-	archive_strcpy(&(aes->aes_mbs), mbs);
-	archive_string_empty(&(aes->aes_utf8));
-	if (aes->aes_wcs) {
-		free((wchar_t *)(uintptr_t)aes->aes_wcs);
-		aes->aes_wcs = NULL;
-	}
-	return (0);
-}
-
-/*
- * The 'update' form tries to proactively update all forms of
- * this string (WCS and MBS) and returns an error if any of
- * them fail.  This is used by the 'pax' handler, for instance,
- * to detect and report character-conversion failures early while
- * still allowing clients to get potentially useful values from
- * the more tolerant lazy conversions.  (get_mbs and get_wcs will
- * strive to give the user something useful, so you can get hopefully
- * usable values even if some of the character conversions are failing.)
- */
-static int
-aes_update_utf8(struct aes *aes, const char *utf8)
-{
-	if (utf8 == NULL) {
-		aes->aes_set = 0;
-		return (1); /* Succeeded in clearing everything. */
-	}
-
-	/* Save the UTF8 string. */
-	archive_strcpy(&(aes->aes_utf8), utf8);
-
-	/* Empty the mbs and wcs strings. */
-	archive_string_empty(&(aes->aes_mbs));
-	if (aes->aes_wcs) {
-		free((wchar_t *)(uintptr_t)aes->aes_wcs);
-		aes->aes_wcs = NULL;
-	}
-
-	aes->aes_set = AES_SET_UTF8;	/* Only UTF8 is set now. */
-
-	/* TODO: We should just do a direct UTF-8 to MBS conversion
-	 * here.  That would be faster, use less space, and give the
-	 * same information.  (If a UTF-8 to MBS conversion succeeds,
-	 * then UTF-8->WCS and Unicode->MBS conversions will both
-	 * succeed.) */
-
-	/* Try converting UTF8 to WCS, return false on failure. */
-	aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8));
-	if (aes->aes_wcs == NULL)
-		return (0);
-	aes->aes_set = AES_SET_UTF8 | AES_SET_WCS; /* Both UTF8 and WCS set. */
-
-	/* Try converting WCS to MBS, return false on failure. */
-	if (archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) == NULL)
-		return (0);
-	aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;
-
-	/* All conversions succeeded. */
-	return (1);
-}
-
-static int
-aes_copy_wcs(struct aes *aes, const wchar_t *wcs)
-{
-	return aes_copy_wcs_len(aes, wcs, wcs == NULL ? 0 : wcslen(wcs));
-}
-
-static int
-aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len)
-{
-	wchar_t *w;
-
-	if (wcs == NULL) {
-		aes->aes_set = 0;
-		return (0);
-	}
-	aes->aes_set = AES_SET_WCS; /* Only WCS form set. */
-	archive_string_empty(&(aes->aes_mbs));
-	archive_string_empty(&(aes->aes_utf8));
-	if (aes->aes_wcs) {
-		free((wchar_t *)(uintptr_t)aes->aes_wcs);
-		aes->aes_wcs = NULL;
-	}
-	w = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
-	if (w == NULL)
-		__archive_errx(1, "No memory for aes_copy_wcs()");
-	wmemcpy(w, wcs, len);
-	w[len] = L'\0';
-	aes->aes_wcs = w;
-	return (0);
-}
 
 /****************************************************************************
  *
@@ -374,15 +155,17 @@
 {
 	if (entry == NULL)
 		return (NULL);
-	aes_clean(&entry->ae_fflags_text);
-	aes_clean(&entry->ae_gname);
-	aes_clean(&entry->ae_hardlink);
-	aes_clean(&entry->ae_pathname);
-	aes_clean(&entry->ae_sourcepath);
-	aes_clean(&entry->ae_symlink);
-	aes_clean(&entry->ae_uname);
-	archive_entry_acl_clear(entry);
+	archive_mstring_clean(&entry->ae_fflags_text);
+	archive_mstring_clean(&entry->ae_gname);
+	archive_mstring_clean(&entry->ae_hardlink);
+	archive_mstring_clean(&entry->ae_pathname);
+	archive_mstring_clean(&entry->ae_sourcepath);
+	archive_mstring_clean(&entry->ae_symlink);
+	archive_mstring_clean(&entry->ae_uname);
+	archive_entry_copy_mac_metadata(entry, NULL, 0);
+	archive_acl_clear(&entry->acl);
 	archive_entry_xattr_clear(entry);
+	archive_entry_sparse_clear(entry);
 	free(entry->stat);
 	memset(entry, 0, sizeof(*entry));
 	return entry;
@@ -392,36 +175,38 @@
 archive_entry_clone(struct archive_entry *entry)
 {
 	struct archive_entry *entry2;
-	struct ae_acl *ap, *ap2;
 	struct ae_xattr *xp;
+	struct ae_sparse *sp;
+	size_t s;
+	const void *p;
 
 	/* Allocate new structure and copy over all of the fields. */
-	entry2 = (struct archive_entry *)malloc(sizeof(*entry2));
+	/* TODO: Should we copy the archive over?  Or require a new archive
+	 * as an argument? */
+	entry2 = archive_entry_new2(entry->archive);
 	if (entry2 == NULL)
 		return (NULL);
-	memset(entry2, 0, sizeof(*entry2));
 	entry2->ae_stat = entry->ae_stat;
 	entry2->ae_fflags_set = entry->ae_fflags_set;
 	entry2->ae_fflags_clear = entry->ae_fflags_clear;
 
-	aes_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text);
-	aes_copy(&entry2->ae_gname, &entry->ae_gname);
-	aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
-	aes_copy(&entry2->ae_pathname, &entry->ae_pathname);
-	aes_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath);
-	aes_copy(&entry2->ae_symlink, &entry->ae_symlink);
+	/* TODO: XXX If clone can have a different archive, what do we do here if
+	 * character sets are different? XXX */
+	archive_mstring_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text);
+	archive_mstring_copy(&entry2->ae_gname, &entry->ae_gname);
+	archive_mstring_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
+	archive_mstring_copy(&entry2->ae_pathname, &entry->ae_pathname);
+	archive_mstring_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath);
+	archive_mstring_copy(&entry2->ae_symlink, &entry->ae_symlink);
 	entry2->ae_set = entry->ae_set;
-	aes_copy(&entry2->ae_uname, &entry->ae_uname);
+	archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname);
 
 	/* Copy ACL data over. */
-	ap = entry->acl_head;
-	while (ap != NULL) {
-		ap2 = acl_new_entry(entry2,
-		    ap->type, ap->permset, ap->tag, ap->id);
-		if (ap2 != NULL)
-			aes_copy(&ap2->name, &ap->name);
-		ap = ap->next;
-	}
+	archive_acl_copy(&entry2->acl, &entry->acl);
+
+	/* Copy Mac OS metadata. */
+	p = archive_entry_mac_metadata(entry, &s);
+	archive_entry_copy_mac_metadata(entry2, p, s);
 
 	/* Copy xattr data over. */
 	xp = entry->xattr_head;
@@ -431,6 +216,14 @@
 		xp = xp->next;
 	}
 
+	/* Copy sparse data over. */
+	sp = entry->sparse_head;
+	while (sp != NULL) {
+		archive_entry_sparse_add_entry(entry2,
+		    sp->offset, sp->length);
+		sp = sp->next;
+	}
+
 	return (entry2);
 }
 
@@ -444,12 +237,19 @@
 struct archive_entry *
 archive_entry_new(void)
 {
+	return archive_entry_new2(NULL);
+}
+
+struct archive_entry *
+archive_entry_new2(struct archive *a)
+{
 	struct archive_entry *entry;
 
 	entry = (struct archive_entry *)malloc(sizeof(*entry));
 	if (entry == NULL)
 		return (NULL);
 	memset(entry, 0, sizeof(*entry));
+	entry->archive = a;
 	return (entry);
 }
 
@@ -521,6 +321,12 @@
 		return (entry->ae_stat.aest_dev);
 }
 
+int
+archive_entry_dev_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_DEV);
+}
+
 dev_t
 archive_entry_devmajor(struct archive_entry *entry)
 {
@@ -542,7 +348,7 @@
 mode_t
 archive_entry_filetype(struct archive_entry *entry)
 {
-	return (AE_IFMT & entry->ae_stat.aest_mode);
+	return (AE_IFMT & entry->acl.mode);
 }
 
 void
@@ -568,8 +374,8 @@
 	const char *f;
 	char *p;
 
-	f = aes_get_mbs(&entry->ae_fflags_text);
-	if (f != NULL)
+	if (archive_mstring_get_mbs(entry->archive,
+	    &entry->ae_fflags_text, &f) == 0 && f != NULL)
 		return (f);
 
 	if (entry->ae_fflags_set == 0  &&  entry->ae_fflags_clear == 0)
@@ -579,13 +385,15 @@
 	if (p == NULL)
 		return (NULL);
 
-	aes_copy_mbs(&entry->ae_fflags_text, p);
+	archive_mstring_copy_mbs(&entry->ae_fflags_text, p);
 	free(p);
-	f = aes_get_mbs(&entry->ae_fflags_text);
-	return (f);
+	if (archive_mstring_get_mbs(entry->archive,
+	    &entry->ae_fflags_text, &f) == 0)
+		return (f);
+	return (NULL);
 }
 
-gid_t
+int64_t
 archive_entry_gid(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_gid);
@@ -594,37 +402,72 @@
 const char *
 archive_entry_gname(struct archive_entry *entry)
 {
-	return (aes_get_mbs(&entry->ae_gname));
+	const char *p;
+	if (archive_mstring_get_mbs(entry->archive, &entry->ae_gname, &p) == 0)
+		return (p);
+	return (NULL);
 }
 
 const wchar_t *
 archive_entry_gname_w(struct archive_entry *entry)
 {
-	return (aes_get_wcs(&entry->ae_gname));
+	const wchar_t *p;
+	if (archive_mstring_get_wcs(entry->archive, &entry->ae_gname, &p) == 0)
+		return (p);
+	return (NULL);
+}
+
+int
+_archive_entry_gname_l(struct archive_entry *entry,
+    const char **p, size_t *len, struct archive_string_conv *sc)
+{
+	return (archive_mstring_get_mbs_l(&entry->ae_gname, p, len, sc));
 }
 
 const char *
 archive_entry_hardlink(struct archive_entry *entry)
 {
-	if (entry->ae_set & AE_SET_HARDLINK)
-		return (aes_get_mbs(&entry->ae_hardlink));
+	const char *p;
+	if ((entry->ae_set & AE_SET_HARDLINK) && archive_mstring_get_mbs(
+	    entry->archive, &entry->ae_hardlink, &p) == 0)
+		return (p);
 	return (NULL);
 }
 
 const wchar_t *
 archive_entry_hardlink_w(struct archive_entry *entry)
 {
-	if (entry->ae_set & AE_SET_HARDLINK)
-		return (aes_get_wcs(&entry->ae_hardlink));
+	const wchar_t *p;
+	if ((entry->ae_set & AE_SET_HARDLINK) && archive_mstring_get_wcs(
+	    entry->archive, &entry->ae_hardlink, &p) == 0)
+		return (p);
 	return (NULL);
 }
 
-ino_t
+int
+_archive_entry_hardlink_l(struct archive_entry *entry,
+    const char **p, size_t *len, struct archive_string_conv *sc)
+{
+	if ((entry->ae_set & AE_SET_HARDLINK) == 0) {
+		*p = NULL;
+		*len = 0;
+		return (0);
+	}
+	return (archive_mstring_get_mbs_l(&entry->ae_hardlink, p, len, sc));
+}
+
+int64_t
 archive_entry_ino(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_ino);
 }
 
+int
+archive_entry_ino_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_INO);
+}
+
 int64_t
 archive_entry_ino64(struct archive_entry *entry)
 {
@@ -634,7 +477,7 @@
 mode_t
 archive_entry_mode(struct archive_entry *entry)
 {
-	return (entry->ae_stat.aest_mode);
+	return (entry->acl.mode);
 }
 
 time_t
@@ -664,13 +507,34 @@
 const char *
 archive_entry_pathname(struct archive_entry *entry)
 {
-	return (aes_get_mbs(&entry->ae_pathname));
+	const char *p;
+	if (archive_mstring_get_mbs(
+	    entry->archive, &entry->ae_pathname, &p) == 0)
+		return (p);
+	return (NULL);
 }
 
 const wchar_t *
 archive_entry_pathname_w(struct archive_entry *entry)
 {
-	return (aes_get_wcs(&entry->ae_pathname));
+	const wchar_t *p;
+	if (archive_mstring_get_wcs(
+	    entry->archive, &entry->ae_pathname, &p) == 0)
+		return (p);
+	return (NULL);
+}
+
+int
+_archive_entry_pathname_l(struct archive_entry *entry,
+    const char **p, size_t *len, struct archive_string_conv *sc)
+{
+	return (archive_mstring_get_mbs_l(&entry->ae_pathname, p, len, sc));
+}
+
+mode_t
+archive_entry_perm(struct archive_entry *entry)
+{
+	return (~AE_IFMT & entry->acl.mode);
 }
 
 dev_t
@@ -716,26 +580,56 @@
 const char *
 archive_entry_sourcepath(struct archive_entry *entry)
 {
-	return (aes_get_mbs(&entry->ae_sourcepath));
+	const char *p;
+	if (archive_mstring_get_mbs(
+	    entry->archive, &entry->ae_sourcepath, &p) == 0)
+		return (p);
+	return (NULL);
+}
+
+const wchar_t *
+archive_entry_sourcepath_w(struct archive_entry *entry)
+{
+	const wchar_t *p;
+	if (archive_mstring_get_wcs(
+	    entry->archive, &entry->ae_sourcepath, &p) == 0)
+		return (p);
+	return (NULL);
 }
 
 const char *
 archive_entry_symlink(struct archive_entry *entry)
 {
-	if (entry->ae_set & AE_SET_SYMLINK)
-		return (aes_get_mbs(&entry->ae_symlink));
+	const char *p;
+	if ((entry->ae_set & AE_SET_SYMLINK) && archive_mstring_get_mbs(
+	    entry->archive, &entry->ae_symlink, &p) == 0)
+		return (p);
 	return (NULL);
 }
 
 const wchar_t *
 archive_entry_symlink_w(struct archive_entry *entry)
 {
-	if (entry->ae_set & AE_SET_SYMLINK)
-		return (aes_get_wcs(&entry->ae_symlink));
+	const wchar_t *p;
+	if ((entry->ae_set & AE_SET_SYMLINK) && archive_mstring_get_wcs(
+	    entry->archive, &entry->ae_symlink, &p) == 0)
+		return (p);
 	return (NULL);
 }
 
-uid_t
+int
+_archive_entry_symlink_l(struct archive_entry *entry,
+    const char **p, size_t *len, struct archive_string_conv *sc)
+{
+	if ((entry->ae_set & AE_SET_SYMLINK) == 0) {
+		*p = NULL;
+		*len = 0;
+		return (0);
+	}
+	return (archive_mstring_get_mbs_l( &entry->ae_symlink, p, len, sc));
+}
+
+int64_t
 archive_entry_uid(struct archive_entry *entry)
 {
 	return (entry->ae_stat.aest_uid);
@@ -744,13 +638,26 @@
 const char *
 archive_entry_uname(struct archive_entry *entry)
 {
-	return (aes_get_mbs(&entry->ae_uname));
+	const char *p;
+	if (archive_mstring_get_mbs(entry->archive, &entry->ae_uname, &p) == 0)
+		return (p);
+	return (NULL);
 }
 
 const wchar_t *
 archive_entry_uname_w(struct archive_entry *entry)
 {
-	return (aes_get_wcs(&entry->ae_uname));
+	const wchar_t *p;
+	if (archive_mstring_get_wcs(entry->archive, &entry->ae_uname, &p) == 0)
+		return (p);
+	return (NULL);
+}
+
+int
+_archive_entry_uname_l(struct archive_entry *entry,
+    const char **p, size_t *len, struct archive_string_conv *sc)
+{
+	return (archive_mstring_get_mbs_l(&entry->ae_uname, p, len, sc));
 }
 
 /*
@@ -761,15 +668,15 @@
 archive_entry_set_filetype(struct archive_entry *entry, unsigned int type)
 {
 	entry->stat_valid = 0;
-	entry->ae_stat.aest_mode &= ~AE_IFMT;
-	entry->ae_stat.aest_mode |= AE_IFMT & type;
+	entry->acl.mode &= ~AE_IFMT;
+	entry->acl.mode |= AE_IFMT & type;
 }
 
 void
 archive_entry_set_fflags(struct archive_entry *entry,
     unsigned long set, unsigned long clear)
 {
-	aes_clean(&entry->ae_fflags_text);
+	archive_mstring_clean(&entry->ae_fflags_text);
 	entry->ae_fflags_set = set;
 	entry->ae_fflags_clear = clear;
 }
@@ -778,7 +685,7 @@
 archive_entry_copy_fflags_text(struct archive_entry *entry,
     const char *flags)
 {
-	aes_copy_mbs(&entry->ae_fflags_text, flags);
+	archive_mstring_copy_mbs(&entry->ae_fflags_text, flags);
 	return (ae_strtofflags(flags,
 		    &entry->ae_fflags_set, &entry->ae_fflags_clear));
 }
@@ -787,13 +694,13 @@
 archive_entry_copy_fflags_text_w(struct archive_entry *entry,
     const wchar_t *flags)
 {
-	aes_copy_wcs(&entry->ae_fflags_text, flags);
+	archive_mstring_copy_wcs(&entry->ae_fflags_text, flags);
 	return (ae_wcstofflags(flags,
 		    &entry->ae_fflags_set, &entry->ae_fflags_clear));
 }
 
 void
-archive_entry_set_gid(struct archive_entry *entry, gid_t g)
+archive_entry_set_gid(struct archive_entry *entry, int64_t g)
 {
 	entry->stat_valid = 0;
 	entry->ae_stat.aest_gid = g;
@@ -802,31 +709,42 @@
 void
 archive_entry_set_gname(struct archive_entry *entry, const char *name)
 {
-	aes_set_mbs(&entry->ae_gname, name);
+	archive_mstring_copy_mbs(&entry->ae_gname, name);
 }
 
 void
 archive_entry_copy_gname(struct archive_entry *entry, const char *name)
 {
-	aes_copy_mbs(&entry->ae_gname, name);
+	archive_mstring_copy_mbs(&entry->ae_gname, name);
 }
 
 void
 archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)
 {
-	aes_copy_wcs(&entry->ae_gname, name);
+	archive_mstring_copy_wcs(&entry->ae_gname, name);
 }
 
 int
 archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name)
 {
-	return (aes_update_utf8(&entry->ae_gname, name));
+	if (archive_mstring_update_utf8(entry->archive,
+	    &entry->ae_gname, name) == 0)
+		return (1);
+	return (0);
+}
+
+int
+_archive_entry_copy_gname_l(struct archive_entry *entry,
+    const char *name, size_t len, struct archive_string_conv *sc)
+{
+	return (archive_mstring_copy_mbs_len_l(&entry->ae_gname, name, len, sc));
 }
 
 void
-archive_entry_set_ino(struct archive_entry *entry, unsigned long ino)
+archive_entry_set_ino(struct archive_entry *entry, int64_t ino)
 {
 	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_INO;
 	entry->ae_stat.aest_ino = ino;
 }
 
@@ -834,13 +752,14 @@
 archive_entry_set_ino64(struct archive_entry *entry, int64_t ino)
 {
 	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_INO;
 	entry->ae_stat.aest_ino = ino;
 }
 
 void
 archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
 {
-	aes_set_mbs(&entry->ae_hardlink, target);
+	archive_mstring_copy_mbs(&entry->ae_hardlink, target);
 	if (target != NULL)
 		entry->ae_set |= AE_SET_HARDLINK;
 	else
@@ -850,7 +769,7 @@
 void
 archive_entry_copy_hardlink(struct archive_entry *entry, const char *target)
 {
-	aes_copy_mbs(&entry->ae_hardlink, target);
+	archive_mstring_copy_mbs(&entry->ae_hardlink, target);
 	if (target != NULL)
 		entry->ae_set |= AE_SET_HARDLINK;
 	else
@@ -860,7 +779,7 @@
 void
 archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target)
 {
-	aes_copy_wcs(&entry->ae_hardlink, target);
+	archive_mstring_copy_wcs(&entry->ae_hardlink, target);
 	if (target != NULL)
 		entry->ae_set |= AE_SET_HARDLINK;
 	else
@@ -874,12 +793,31 @@
 		entry->ae_set |= AE_SET_HARDLINK;
 	else
 		entry->ae_set &= ~AE_SET_HARDLINK;
-	return (aes_update_utf8(&entry->ae_hardlink, target));
+	if (archive_mstring_update_utf8(entry->archive,
+	    &entry->ae_hardlink, target) == 0)
+		return (1);
+	return (0);
+}
+
+int
+_archive_entry_copy_hardlink_l(struct archive_entry *entry,
+    const char *target, size_t len, struct archive_string_conv *sc)
+{
+	int r;
+
+	r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink,
+	    target, len, sc);
+	if (target != NULL && r == 0)
+		entry->ae_set |= AE_SET_HARDLINK;
+	else
+		entry->ae_set &= ~AE_SET_HARDLINK;
+	return (r);
 }
 
 void
 archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
 {
+	FIX_NS(t, ns);
 	entry->stat_valid = 0;
 	entry->ae_set |= AE_SET_ATIME;
 	entry->ae_stat.aest_atime = t;
@@ -894,11 +832,12 @@
 }
 
 void
-archive_entry_set_birthtime(struct archive_entry *entry, time_t m, long ns)
+archive_entry_set_birthtime(struct archive_entry *entry, time_t t, long ns)
 {
+	FIX_NS(t, ns);
 	entry->stat_valid = 0;
 	entry->ae_set |= AE_SET_BIRTHTIME;
-	entry->ae_stat.aest_birthtime = m;
+	entry->ae_stat.aest_birthtime = t;
 	entry->ae_stat.aest_birthtime_nsec = ns;
 }
 
@@ -912,6 +851,7 @@
 void
 archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
 {
+	FIX_NS(t, ns);
 	entry->stat_valid = 0;
 	entry->ae_set |= AE_SET_CTIME;
 	entry->ae_stat.aest_ctime = t;
@@ -929,6 +869,7 @@
 archive_entry_set_dev(struct archive_entry *entry, dev_t d)
 {
 	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_DEV;
 	entry->ae_stat.aest_dev_is_broken_down = 0;
 	entry->ae_stat.aest_dev = d;
 }
@@ -937,6 +878,7 @@
 archive_entry_set_devmajor(struct archive_entry *entry, dev_t m)
 {
 	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_DEV;
 	entry->ae_stat.aest_dev_is_broken_down = 1;
 	entry->ae_stat.aest_devmajor = m;
 }
@@ -945,6 +887,7 @@
 archive_entry_set_devminor(struct archive_entry *entry, dev_t m)
 {
 	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_DEV;
 	entry->ae_stat.aest_dev_is_broken_down = 1;
 	entry->ae_stat.aest_devminor = m;
 }
@@ -954,9 +897,9 @@
 archive_entry_set_link(struct archive_entry *entry, const char *target)
 {
 	if (entry->ae_set & AE_SET_SYMLINK)
-		aes_set_mbs(&entry->ae_symlink, target);
+		archive_mstring_copy_mbs(&entry->ae_symlink, target);
 	else
-		aes_set_mbs(&entry->ae_hardlink, target);
+		archive_mstring_copy_mbs(&entry->ae_hardlink, target);
 }
 
 /* Set symlink if symlink is already set, else set hardlink. */
@@ -964,9 +907,9 @@
 archive_entry_copy_link(struct archive_entry *entry, const char *target)
 {
 	if (entry->ae_set & AE_SET_SYMLINK)
-		aes_copy_mbs(&entry->ae_symlink, target);
+		archive_mstring_copy_mbs(&entry->ae_symlink, target);
 	else
-		aes_copy_mbs(&entry->ae_hardlink, target);
+		archive_mstring_copy_mbs(&entry->ae_hardlink, target);
 }
 
 /* Set symlink if symlink is already set, else set hardlink. */
@@ -974,33 +917,53 @@
 archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)
 {
 	if (entry->ae_set & AE_SET_SYMLINK)
-		aes_copy_wcs(&entry->ae_symlink, target);
+		archive_mstring_copy_wcs(&entry->ae_symlink, target);
 	else
-		aes_copy_wcs(&entry->ae_hardlink, target);
+		archive_mstring_copy_wcs(&entry->ae_hardlink, target);
 }
 
 int
 archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
 {
+	int r;
 	if (entry->ae_set & AE_SET_SYMLINK)
-		return (aes_update_utf8(&entry->ae_symlink, target));
+		r = archive_mstring_update_utf8(entry->archive,
+		    &entry->ae_symlink, target);
 	else
-		return (aes_update_utf8(&entry->ae_hardlink, target));
+		r = archive_mstring_update_utf8(entry->archive,
+		    &entry->ae_hardlink, target);
+	return ((r == 0)? 1: 0);
+}
+
+int
+_archive_entry_copy_link_l(struct archive_entry *entry,
+    const char *target, size_t len, struct archive_string_conv *sc)
+{
+	int r;
+
+	if (entry->ae_set & AE_SET_SYMLINK)
+		r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink,
+		    target, len, sc);
+	else
+		r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink,
+		    target, len, sc);
+	return (r);
 }
 
 void
 archive_entry_set_mode(struct archive_entry *entry, mode_t m)
 {
 	entry->stat_valid = 0;
-	entry->ae_stat.aest_mode = m;
+	entry->acl.mode = m;
 }
 
 void
-archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns)
+archive_entry_set_mtime(struct archive_entry *entry, time_t t, long ns)
 {
+	FIX_NS(t, ns);
 	entry->stat_valid = 0;
 	entry->ae_set |= AE_SET_MTIME;
-	entry->ae_stat.aest_mtime = m;
+	entry->ae_stat.aest_mtime = t;
 	entry->ae_stat.aest_mtime_nsec = ns;
 }
 
@@ -1021,33 +984,44 @@
 void
 archive_entry_set_pathname(struct archive_entry *entry, const char *name)
 {
-	aes_set_mbs(&entry->ae_pathname, name);
+	archive_mstring_copy_mbs(&entry->ae_pathname, name);
 }
 
 void
 archive_entry_copy_pathname(struct archive_entry *entry, const char *name)
 {
-	aes_copy_mbs(&entry->ae_pathname, name);
+	archive_mstring_copy_mbs(&entry->ae_pathname, name);
 }
 
 void
 archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)
 {
-	aes_copy_wcs(&entry->ae_pathname, name);
+	archive_mstring_copy_wcs(&entry->ae_pathname, name);
 }
 
 int
 archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name)
 {
-	return (aes_update_utf8(&entry->ae_pathname, name));
+	if (archive_mstring_update_utf8(entry->archive,
+	    &entry->ae_pathname, name) == 0)
+		return (1);
+	return (0);
+}
+
+int
+_archive_entry_copy_pathname_l(struct archive_entry *entry,
+    const char *name, size_t len, struct archive_string_conv *sc)
+{
+	return (archive_mstring_copy_mbs_len_l(&entry->ae_pathname,
+	    name, len, sc));
 }
 
 void
 archive_entry_set_perm(struct archive_entry *entry, mode_t p)
 {
 	entry->stat_valid = 0;
-	entry->ae_stat.aest_mode &= AE_IFMT;
-	entry->ae_stat.aest_mode |= ~AE_IFMT & p;
+	entry->acl.mode &= AE_IFMT;
+	entry->acl.mode |= ~AE_IFMT & p;
 }
 
 void
@@ -1092,13 +1066,19 @@
 void
 archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path)
 {
-	aes_set_mbs(&entry->ae_sourcepath, path);
+	archive_mstring_copy_mbs(&entry->ae_sourcepath, path);
+}
+
+void
+archive_entry_copy_sourcepath_w(struct archive_entry *entry, const wchar_t *path)
+{
+	archive_mstring_copy_wcs(&entry->ae_sourcepath, path);
 }
 
 void
 archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)
 {
-	aes_set_mbs(&entry->ae_symlink, linkname);
+	archive_mstring_copy_mbs(&entry->ae_symlink, linkname);
 	if (linkname != NULL)
 		entry->ae_set |= AE_SET_SYMLINK;
 	else
@@ -1108,7 +1088,7 @@
 void
 archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname)
 {
-	aes_copy_mbs(&entry->ae_symlink, linkname);
+	archive_mstring_copy_mbs(&entry->ae_symlink, linkname);
 	if (linkname != NULL)
 		entry->ae_set |= AE_SET_SYMLINK;
 	else
@@ -1118,7 +1098,7 @@
 void
 archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)
 {
-	aes_copy_wcs(&entry->ae_symlink, linkname);
+	archive_mstring_copy_wcs(&entry->ae_symlink, linkname);
 	if (linkname != NULL)
 		entry->ae_set |= AE_SET_SYMLINK;
 	else
@@ -1132,11 +1112,29 @@
 		entry->ae_set |= AE_SET_SYMLINK;
 	else
 		entry->ae_set &= ~AE_SET_SYMLINK;
-	return (aes_update_utf8(&entry->ae_symlink, linkname));
+	if (archive_mstring_update_utf8(entry->archive,
+	    &entry->ae_symlink, linkname) == 0)
+		return (1);
+	return (0);
+}
+
+int
+_archive_entry_copy_symlink_l(struct archive_entry *entry,
+    const char *linkname, size_t len, struct archive_string_conv *sc)
+{
+	int r;
+
+	r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink,
+	    linkname, len, sc);
+	if (linkname != NULL && r == 0)
+		entry->ae_set |= AE_SET_SYMLINK;
+	else
+		entry->ae_set &= ~AE_SET_SYMLINK;
+	return (r);
 }
 
 void
-archive_entry_set_uid(struct archive_entry *entry, uid_t u)
+archive_entry_set_uid(struct archive_entry *entry, int64_t u)
 {
 	entry->stat_valid = 0;
 	entry->ae_stat.aest_uid = u;
@@ -1145,25 +1143,60 @@
 void
 archive_entry_set_uname(struct archive_entry *entry, const char *name)
 {
-	aes_set_mbs(&entry->ae_uname, name);
+	archive_mstring_copy_mbs(&entry->ae_uname, name);
 }
 
 void
 archive_entry_copy_uname(struct archive_entry *entry, const char *name)
 {
-	aes_copy_mbs(&entry->ae_uname, name);
+	archive_mstring_copy_mbs(&entry->ae_uname, name);
 }
 
 void
 archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)
 {
-	aes_copy_wcs(&entry->ae_uname, name);
+	archive_mstring_copy_wcs(&entry->ae_uname, name);
 }
 
 int
 archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name)
 {
-	return (aes_update_utf8(&entry->ae_uname, name));
+	if (archive_mstring_update_utf8(entry->archive,
+	    &entry->ae_uname, name) == 0)
+		return (1);
+	return (0);
+}
+
+int
+_archive_entry_copy_uname_l(struct archive_entry *entry,
+    const char *name, size_t len, struct archive_string_conv *sc)
+{
+	return (archive_mstring_copy_mbs_len_l(&entry->ae_uname,
+	    name, len, sc));
+}
+
+const void *
+archive_entry_mac_metadata(struct archive_entry *entry, size_t *s)
+{
+  *s = entry->mac_metadata_size;
+  return entry->mac_metadata;
+}
+
+void
+archive_entry_copy_mac_metadata(struct archive_entry *entry,
+    const void *p, size_t s)
+{
+  free(entry->mac_metadata);
+  if (p == NULL || s == 0) {
+    entry->mac_metadata = NULL;
+    entry->mac_metadata_size = 0;
+  } else {
+    entry->mac_metadata_size = s;
+    entry->mac_metadata = malloc(s);
+    if (entry->mac_metadata == NULL)
+      abort();
+    memcpy(entry->mac_metadata, p, s);
+  }
 }
 
 /*
@@ -1175,148 +1208,37 @@
  * uninitiated.
  */
 
+struct archive_acl *
+archive_entry_acl(struct archive_entry *entry)
+{
+	return &entry->acl;
+}
+
 void
 archive_entry_acl_clear(struct archive_entry *entry)
 {
-	struct ae_acl	*ap;
-
-	while (entry->acl_head != NULL) {
-		ap = entry->acl_head->next;
-		aes_clean(&entry->acl_head->name);
-		free(entry->acl_head);
-		entry->acl_head = ap;
-	}
-	if (entry->acl_text_w != NULL) {
-		free(entry->acl_text_w);
-		entry->acl_text_w = NULL;
-	}
-	entry->acl_p = NULL;
-	entry->acl_state = 0; /* Not counting. */
+	archive_acl_clear(&entry->acl);
 }
 
 /*
  * Add a single ACL entry to the internal list of ACL data.
  */
-void
+int
 archive_entry_acl_add_entry(struct archive_entry *entry,
     int type, int permset, int tag, int id, const char *name)
 {
-	struct ae_acl *ap;
-
-	if (acl_special(entry, type, permset, tag) == 0)
-		return;
-	ap = acl_new_entry(entry, type, permset, tag, id);
-	if (ap == NULL) {
-		/* XXX Error XXX */
-		return;
-	}
-	if (name != NULL  &&  *name != '\0')
-		aes_copy_mbs(&ap->name, name);
-	else
-		aes_clean(&ap->name);
+	return archive_acl_add_entry(&entry->acl, type, permset, tag, id, name);
 }
 
 /*
  * As above, but with a wide-character name.
  */
-void
+int
 archive_entry_acl_add_entry_w(struct archive_entry *entry,
     int type, int permset, int tag, int id, const wchar_t *name)
 {
-	archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, name, wcslen(name));
-}
-
-static void
-archive_entry_acl_add_entry_w_len(struct archive_entry *entry,
-    int type, int permset, int tag, int id, const wchar_t *name, size_t len)
-{
-	struct ae_acl *ap;
-
-	if (acl_special(entry, type, permset, tag) == 0)
-		return;
-	ap = acl_new_entry(entry, type, permset, tag, id);
-	if (ap == NULL) {
-		/* XXX Error XXX */
-		return;
-	}
-	if (name != NULL  &&  *name != L'\0' && len > 0)
-		aes_copy_wcs_len(&ap->name, name, len);
-	else
-		aes_clean(&ap->name);
-}
-
-/*
- * If this ACL entry is part of the standard POSIX permissions set,
- * store the permissions in the stat structure and return zero.
- */
-static int
-acl_special(struct archive_entry *entry, int type, int permset, int tag)
-{
-	if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
-		switch (tag) {
-		case ARCHIVE_ENTRY_ACL_USER_OBJ:
-			entry->ae_stat.aest_mode &= ~0700;
-			entry->ae_stat.aest_mode |= (permset & 7) << 6;
-			return (0);
-		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
-			entry->ae_stat.aest_mode &= ~0070;
-			entry->ae_stat.aest_mode |= (permset & 7) << 3;
-			return (0);
-		case ARCHIVE_ENTRY_ACL_OTHER:
-			entry->ae_stat.aest_mode &= ~0007;
-			entry->ae_stat.aest_mode |= permset & 7;
-			return (0);
-		}
-	}
-	return (1);
-}
-
-/*
- * Allocate and populate a new ACL entry with everything but the
- * name.
- */
-static struct ae_acl *
-acl_new_entry(struct archive_entry *entry,
-    int type, int permset, int tag, int id)
-{
-	struct ae_acl *ap, *aq;
-
-	if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS &&
-	    type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
-		return (NULL);
-	if (entry->acl_text_w != NULL) {
-		free(entry->acl_text_w);
-		entry->acl_text_w = NULL;
-	}
-
-	/* XXX TODO: More sanity-checks on the arguments XXX */
-
-	/* If there's a matching entry already in the list, overwrite it. */
-	ap = entry->acl_head;
-	aq = NULL;
-	while (ap != NULL) {
-		if (ap->type == type && ap->tag == tag && ap->id == id) {
-			ap->permset = permset;
-			return (ap);
-		}
-		aq = ap;
-		ap = ap->next;
-	}
-
-	/* Add a new entry to the end of the list. */
-	ap = (struct ae_acl *)malloc(sizeof(*ap));
-	if (ap == NULL)
-		return (NULL);
-	memset(ap, 0, sizeof(*ap));
-	if (aq == NULL)
-		entry->acl_head = ap;
-	else
-		aq->next = ap;
-	ap->type = type;
-	ap->tag = tag;
-	ap->id = id;
-	ap->permset = permset;
-	return (ap);
+	return archive_acl_add_entry_w_len(&entry->acl,
+	    type, permset, tag, id, name, wcslen(name));
 }
 
 /*
@@ -1325,20 +1247,7 @@
 int
 archive_entry_acl_count(struct archive_entry *entry, int want_type)
 {
-	int count;
-	struct ae_acl *ap;
-
-	count = 0;
-	ap = entry->acl_head;
-	while (ap != NULL) {
-		if ((ap->type & want_type) != 0)
-			count++;
-		ap = ap->next;
-	}
-
-	if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
-		count += 3;
-	return (count);
+	return archive_acl_count(&entry->acl, want_type);
 }
 
 /*
@@ -1349,93 +1258,18 @@
 int
 archive_entry_acl_reset(struct archive_entry *entry, int want_type)
 {
-	int count, cutoff;
-
-	count = archive_entry_acl_count(entry, want_type);
-
-	/*
-	 * If the only entries are the three standard ones,
-	 * then don't return any ACL data.  (In this case,
-	 * client can just use chmod(2) to set permissions.)
-	 */
-	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
-		cutoff = 3;
-	else
-		cutoff = 0;
-
-	if (count > cutoff)
-		entry->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
-	else
-		entry->acl_state = 0;
-	entry->acl_p = entry->acl_head;
-	return (count);
+	return archive_acl_reset(&entry->acl, want_type);
 }
 
 /*
  * Return the next ACL entry in the list.  Fake entries for the
  * standard permissions and include them in the returned list.
  */
-
 int
 archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
     int *permset, int *tag, int *id, const char **name)
 {
-	*name = NULL;
-	*id = -1;
-
-	/*
-	 * The acl_state is either zero (no entries available), -1
-	 * (reading from list), or an entry type (retrieve that type
-	 * from ae_stat.aest_mode).
-	 */
-	if (entry->acl_state == 0)
-		return (ARCHIVE_WARN);
-
-	/* The first three access entries are special. */
-	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
-		switch (entry->acl_state) {
-		case ARCHIVE_ENTRY_ACL_USER_OBJ:
-			*permset = (entry->ae_stat.aest_mode >> 6) & 7;
-			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
-			*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
-			entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
-			return (ARCHIVE_OK);
-		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
-			*permset = (entry->ae_stat.aest_mode >> 3) & 7;
-			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
-			*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
-			entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
-			return (ARCHIVE_OK);
-		case ARCHIVE_ENTRY_ACL_OTHER:
-			*permset = entry->ae_stat.aest_mode & 7;
-			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
-			*tag = ARCHIVE_ENTRY_ACL_OTHER;
-			entry->acl_state = -1;
-			entry->acl_p = entry->acl_head;
-			return (ARCHIVE_OK);
-		default:
-			break;
-		}
-	}
-
-	while (entry->acl_p != NULL && (entry->acl_p->type & want_type) == 0)
-		entry->acl_p = entry->acl_p->next;
-	if (entry->acl_p == NULL) {
-		entry->acl_state = 0;
-		*type = 0;
-		*permset = 0;
-		*tag = 0;
-		*id = -1;
-		*name = NULL;
-		return (ARCHIVE_EOF); /* End of ACL entries. */
-	}
-	*type = entry->acl_p->type;
-	*permset = entry->acl_p->permset;
-	*tag = entry->acl_p->tag;
-	*id = entry->acl_p->id;
-	*name = aes_get_mbs(&entry->acl_p->name);
-	entry->acl_p = entry->acl_p->next;
-	return (ARCHIVE_OK);
+	return archive_acl_next(entry->archive, &entry->acl, want_type, type, permset, tag, id, name);
 }
 
 /*
@@ -1445,412 +1279,27 @@
 const wchar_t *
 archive_entry_acl_text_w(struct archive_entry *entry, int flags)
 {
-	int count;
-	size_t length;
-	const wchar_t *wname;
-	const wchar_t *prefix;
-	wchar_t separator;
-	struct ae_acl *ap;
-	int id;
-	wchar_t *wp;
-
-	if (entry->acl_text_w != NULL) {
-		free (entry->acl_text_w);
-		entry->acl_text_w = NULL;
-	}
-
-	separator = L',';
-	count = 0;
-	length = 0;
-	ap = entry->acl_head;
-	while (ap != NULL) {
-		if ((ap->type & flags) != 0) {
-			count++;
-			if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
-			    (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
-				length += 8; /* "default:" */
-			length += 5; /* tag name */
-			length += 1; /* colon */
-			wname = aes_get_wcs(&ap->name);
-			if (wname != NULL)
-				length += wcslen(wname);
-			else
-				length += sizeof(uid_t) * 3 + 1;
-			length ++; /* colon */
-			length += 3; /* rwx */
-			length += 1; /* colon */
-			length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
-			length ++; /* newline */
-		}
-		ap = ap->next;
-	}
-
-	if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
-		length += 10; /* "user::rwx\n" */
-		length += 11; /* "group::rwx\n" */
-		length += 11; /* "other::rwx\n" */
-	}
-
-	if (count == 0)
-		return (NULL);
-
-	/* Now, allocate the string and actually populate it. */
-	wp = entry->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
-	if (wp == NULL)
-		__archive_errx(1, "No memory to generate the text version of the ACL");
-	count = 0;
-	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
-		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
-		    entry->ae_stat.aest_mode & 0700, -1);
-		*wp++ = ',';
-		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
-		    entry->ae_stat.aest_mode & 0070, -1);
-		*wp++ = ',';
-		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
-		    entry->ae_stat.aest_mode & 0007, -1);
-		count += 3;
-
-		ap = entry->acl_head;
-		while (ap != NULL) {
-			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
-				wname = aes_get_wcs(&ap->name);
-				*wp++ = separator;
-				if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
-					id = ap->id;
-				else
-					id = -1;
-				append_entry_w(&wp, NULL, ap->tag, wname,
-				    ap->permset, id);
-				count++;
-			}
-			ap = ap->next;
-		}
-	}
-
-
-	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
-		if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
-			prefix = L"default:";
-		else
-			prefix = NULL;
-		ap = entry->acl_head;
-		count = 0;
-		while (ap != NULL) {
-			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
-				wname = aes_get_wcs(&ap->name);
-				if (count > 0)
-					*wp++ = separator;
-				if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
-					id = ap->id;
-				else
-					id = -1;
-				append_entry_w(&wp, prefix, ap->tag,
-				    wname, ap->permset, id);
-				count ++;
-			}
-			ap = ap->next;
-		}
-	}
-
-	return (entry->acl_text_w);
+	return archive_acl_text_w(entry->archive, &entry->acl, flags);
 }
 
-static void
-append_id_w(wchar_t **wp, int id)
+const char *
+archive_entry_acl_text(struct archive_entry *entry, int flags)
 {
-	if (id < 0)
-		id = 0;
-	if (id > 9)
-		append_id_w(wp, id / 10);
-	*(*wp)++ = L"0123456789"[id % 10];
+	const char *p;
+	if (archive_acl_text_l(&entry->acl, flags, &p, NULL, NULL) != 0
+	    && errno == ENOMEM)
+		return (NULL);
+	return (p);
 }
 
-static void
-append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
-    const wchar_t *wname, int perm, int id)
+int
+_archive_entry_acl_text_l(struct archive_entry *entry, int flags,
+    const char **acl_text, size_t *len, struct archive_string_conv *sc)
 {
-	if (prefix != NULL) {
-		wcscpy(*wp, prefix);
-		*wp += wcslen(*wp);
-	}
-	switch (tag) {
-	case ARCHIVE_ENTRY_ACL_USER_OBJ:
-		wname = NULL;
-		id = -1;
-		/* FALLTHROUGH */
-	case ARCHIVE_ENTRY_ACL_USER:
-		wcscpy(*wp, L"user");
-		break;
-	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
-		wname = NULL;
-		id = -1;
-		/* FALLTHROUGH */
-	case ARCHIVE_ENTRY_ACL_GROUP:
-		wcscpy(*wp, L"group");
-		break;
-	case ARCHIVE_ENTRY_ACL_MASK:
-		wcscpy(*wp, L"mask");
-		wname = NULL;
-		id = -1;
-		break;
-	case ARCHIVE_ENTRY_ACL_OTHER:
-		wcscpy(*wp, L"other");
-		wname = NULL;
-		id = -1;
-		break;
-	}
-	*wp += wcslen(*wp);
-	*(*wp)++ = L':';
-	if (wname != NULL) {
-		wcscpy(*wp, wname);
-		*wp += wcslen(*wp);
-	} else if (tag == ARCHIVE_ENTRY_ACL_USER
-	    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
-		append_id_w(wp, id);
-		id = -1;
-	}
-	*(*wp)++ = L':';
-	*(*wp)++ = (perm & 0444) ? L'r' : L'-';
-	*(*wp)++ = (perm & 0222) ? L'w' : L'-';
-	*(*wp)++ = (perm & 0111) ? L'x' : L'-';
-	if (id != -1) {
-		*(*wp)++ = L':';
-		append_id_w(wp, id);
-	}
-	**wp = L'\0';
+	return (archive_acl_text_l(&entry->acl, flags, acl_text, len, sc));
 }
 
 /*
- * Parse a textual ACL.  This automatically recognizes and supports
- * extensions described above.  The 'type' argument is used to
- * indicate the type that should be used for any entries not
- * explicitly marked as "default:".
- */
-int
-__archive_entry_acl_parse_w(struct archive_entry *entry,
-    const wchar_t *text, int default_type)
-{
-	struct {
-		const wchar_t *start;
-		const wchar_t *end;
-	} field[4], name;
-
-	int fields, n;
-	int type, tag, permset, id;
-	wchar_t sep;
-
-	while (text != NULL  &&  *text != L'\0') {
-		/*
-		 * Parse the fields out of the next entry,
-		 * advance 'text' to start of next entry.
-		 */
-		fields = 0;
-		do {
-			const wchar_t *start, *end;
-			next_field_w(&text, &start, &end, &sep);
-			if (fields < 4) {
-				field[fields].start = start;
-				field[fields].end = end;
-			}
-			++fields;
-		} while (sep == L':');
-
-		/* Set remaining fields to blank. */
-		for (n = fields; n < 4; ++n)
-			field[n].start = field[n].end = NULL;
-
-		/* Check for a numeric ID in field 1 or 3. */
-		id = -1;
-		isint_w(field[1].start, field[1].end, &id);
-		/* Field 3 is optional. */
-		if (id == -1 && fields > 3)
-			isint_w(field[3].start, field[3].end, &id);
-
-		/*
-		 * Solaris extension:  "defaultuser::rwx" is the
-		 * default ACL corresponding to "user::rwx", etc.
-		 */
-		if (field[0].end - field[0].start > 7
-		    && wmemcmp(field[0].start, L"default", 7) == 0) {
-			type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
-			field[0].start += 7;
-		} else
-			type = default_type;
-
-		name.start = name.end = NULL;
-		if (prefix_w(field[0].start, field[0].end, L"user")) {
-			if (!ismode_w(field[2].start, field[2].end, &permset))
-				return (ARCHIVE_WARN);
-			if (id != -1 || field[1].start < field[1].end) {
-				tag = ARCHIVE_ENTRY_ACL_USER;
-				name = field[1];
-			} else
-				tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
-		} else if (prefix_w(field[0].start, field[0].end, L"group")) {
-			if (!ismode_w(field[2].start, field[2].end, &permset))
-				return (ARCHIVE_WARN);
-			if (id != -1 || field[1].start < field[1].end) {
-				tag = ARCHIVE_ENTRY_ACL_GROUP;
-				name = field[1];
-			} else
-				tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
-		} else if (prefix_w(field[0].start, field[0].end, L"other")) {
-			if (fields == 2
-			    && field[1].start < field[1].end
-			    && ismode_w(field[1].start, field[1].end, &permset)) {
-				/* This is Solaris-style "other:rwx" */
-			} else if (fields == 3
-			    && field[1].start == field[1].end
-			    && field[2].start < field[2].end
-			    && ismode_w(field[2].start, field[2].end, &permset)) {
-				/* This is FreeBSD-style "other::rwx" */
-			} else
-				return (ARCHIVE_WARN);
-			tag = ARCHIVE_ENTRY_ACL_OTHER;
-		} else if (prefix_w(field[0].start, field[0].end, L"mask")) {
-			if (fields == 2
-			    && field[1].start < field[1].end
-			    && ismode_w(field[1].start, field[1].end, &permset)) {
-				/* This is Solaris-style "mask:rwx" */
-			} else if (fields == 3
-			    && field[1].start == field[1].end
-			    && field[2].start < field[2].end
-			    && ismode_w(field[2].start, field[2].end, &permset)) {
-				/* This is FreeBSD-style "mask::rwx" */
-			} else
-				return (ARCHIVE_WARN);
-			tag = ARCHIVE_ENTRY_ACL_MASK;
-		} else
-			return (ARCHIVE_WARN);
-
-		/* Add entry to the internal list. */
-		archive_entry_acl_add_entry_w_len(entry, type, permset,
-		    tag, id, name.start, name.end - name.start);
-	}
-	return (ARCHIVE_OK);
-}
-
-/*
- * Parse a string to a positive decimal integer.  Returns true if
- * the string is non-empty and consists only of decimal digits,
- * false otherwise.
- */
-static int
-isint_w(const wchar_t *start, const wchar_t *end, int *result)
-{
-	int n = 0;
-	if (start >= end)
-		return (0);
-	while (start < end) {
-		if (*start < '0' || *start > '9')
-			return (0);
-		if (n > (INT_MAX / 10))
-			n = INT_MAX;
-		else {
-			n *= 10;
-			n += *start - '0';
-		}
-		start++;
-	}
-	*result = n;
-	return (1);
-}
-
-/*
- * Parse a string as a mode field.  Returns true if
- * the string is non-empty and consists only of mode characters,
- * false otherwise.
- */
-static int
-ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
-{
-	const wchar_t *p;
-
-	if (start >= end)
-		return (0);
-	p = start;
-	*permset = 0;
-	while (p < end) {
-		switch (*p++) {
-		case 'r': case 'R':
-			*permset |= ARCHIVE_ENTRY_ACL_READ;
-			break;
-		case 'w': case 'W':
-			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
-			break;
-		case 'x': case 'X':
-			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
-			break;
-		case '-':
-			break;
-		default:
-			return (0);
-		}
-	}
-	return (1);
-}
-
-/*
- * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
- * to point to just after the separator.  *start points to the first
- * character of the matched text and *end just after the last
- * character of the matched identifier.  In particular *end - *start
- * is the length of the field body, not including leading or trailing
- * whitespace.
- */
-static void
-next_field_w(const wchar_t **wp, const wchar_t **start,
-    const wchar_t **end, wchar_t *sep)
-{
-	/* Skip leading whitespace to find start of field. */
-	while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
-		(*wp)++;
-	}
-	*start = *wp;
-
-	/* Scan for the separator. */
-	while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
-	    **wp != L'\n') {
-		(*wp)++;
-	}
-	*sep = **wp;
-
-	/* Trim trailing whitespace to locate end of field. */
-	*end = *wp - 1;
-	while (**end == L' ' || **end == L'\t' || **end == L'\n') {
-		(*end)--;
-	}
-	(*end)++;
-
-	/* Adjust scanner location. */
-	if (**wp != L'\0')
-		(*wp)++;
-}
-
-/*
- * Return true if the characters [start...end) are a prefix of 'test'.
- * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
- */
-static int
-prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
-{
-	if (start == end)
-		return (0);
-
-	if (*start++ != *test++)
-		return (0);
-
-	while (start < end  &&  *start++ == *test++)
-		;
-
-	if (start < end)
-		return (0);
-
-	return (1);
-}
-
-
-/*
  * Following code is modified from UC Berkeley sources, and
  * is subject to the following copyright notice.
  */
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_entry.h
--- a/head/contrib/libarchive/libarchive/archive_entry.h	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_entry.h	Fri Mar 02 16:54:40 2012 +0200
@@ -22,12 +22,15 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/contrib/libarchive/libarchive/archive_entry.h 228763 2011-12-21 11:13:29Z mm $
+ * $FreeBSD: head/contrib/libarchive/libarchive/archive_entry.h 232153 2012-02-25 10:58:02Z mm $
  */
 
 #ifndef ARCHIVE_ENTRY_H_INCLUDED
 #define	ARCHIVE_ENTRY_H_INCLUDED
 
+/* Note: Compiler will complain if this does not match archive.h! */
+#define	ARCHIVE_VERSION_NUMBER 3000003
+
 /*
  * Note: archive_entry.h is for use outside of libarchive; the
  * configuration headers (config.h, archive_platform.h, etc.) are
@@ -49,30 +52,31 @@
 #if defined(_WIN32) && !defined(__CYGWIN__)
 #define	__LA_INT64_T	__int64
 # if defined(__BORLANDC__)
-#  define	__LA_UID_T	uid_t
-#  define	__LA_GID_T	gid_t
+#  define	__LA_UID_T	uid_t  /* Remove in libarchive 3.2 */
+#  define	__LA_GID_T	gid_t  /* Remove in libarchive 3.2 */
 #  define	__LA_DEV_T	dev_t
 #  define	__LA_MODE_T	mode_t
 # else
-#  define	__LA_UID_T	short
-#  define	__LA_GID_T	short
+#  define	__LA_UID_T	short  /* Remove in libarchive 3.2 */
+#  define	__LA_GID_T	short  /* Remove in libarchive 3.2 */
 #  define	__LA_DEV_T	unsigned int
 #  define	__LA_MODE_T	unsigned short
 # endif
 #else
 #include <unistd.h>
-#define	__LA_INT64_T	int64_t
-#define	__LA_UID_T	uid_t
-#define	__LA_GID_T	gid_t
-#define	__LA_DEV_T	dev_t
-#define	__LA_MODE_T	mode_t
+# if defined(_SCO_DS)
+#  define	__LA_INT64_T	long long
+# else
+#  define	__LA_INT64_T	int64_t
+# endif
+# define	__LA_UID_T	uid_t /* Remove in libarchive 3.2 */
+# define	__LA_GID_T	gid_t /* Remove in libarchive 3.2 */
+# define	__LA_DEV_T	dev_t
+# define	__LA_MODE_T	mode_t
 #endif
 
 /*
- * XXX Is this defined for all Windows compilers?  If so, in what
- * header?  It would be nice to remove the __LA_INO_T indirection and
- * just use plain ino_t everywhere.  Likewise for the other types just
- * above.
+ * Remove this for libarchive 3.2, since ino_t is no longer used.
  */
 #define	__LA_INO_T	ino_t
 
@@ -91,7 +95,7 @@
 #  endif
 # else
 #  ifdef __GNUC__
-#   define __LA_DECL	__attribute__((dllimport)) extern
+#   define __LA_DECL
 #  else
 #   define __LA_DECL	__declspec(dllimport)
 #  endif
@@ -121,6 +125,7 @@
  * applications (e.g., a package manager could attach special
  * package-management attributes to each entry).
  */
+struct archive;
 struct archive_entry;
 
 /*
@@ -164,6 +169,15 @@
 __LA_DECL struct archive_entry	*archive_entry_new(void);
 
 /*
+ * This form of archive_entry_new2() will pull character-set
+ * conversion information from the specified archive handle.  The
+ * older archive_entry_new(void) form is equivalent to calling
+ * archive_entry_new2(NULL) and will result in the use of an internal
+ * default character-set conversion.
+ */
+__LA_DECL struct archive_entry	*archive_entry_new2(struct archive *);
+
+/*
  * Retrieve fields from an archive_entry.
  *
  * There are a number of implicit conversions among these fields.  For
@@ -192,6 +206,7 @@
 __LA_DECL long		 archive_entry_ctime_nsec(struct archive_entry *);
 __LA_DECL int		 archive_entry_ctime_is_set(struct archive_entry *);
 __LA_DECL dev_t		 archive_entry_dev(struct archive_entry *);
+__LA_DECL int		 archive_entry_dev_is_set(struct archive_entry *);
 __LA_DECL dev_t		 archive_entry_devmajor(struct archive_entry *);
 __LA_DECL dev_t		 archive_entry_devminor(struct archive_entry *);
 __LA_DECL __LA_MODE_T	 archive_entry_filetype(struct archive_entry *);
@@ -199,13 +214,14 @@
 			    unsigned long * /* set */,
 			    unsigned long * /* clear */);
 __LA_DECL const char	*archive_entry_fflags_text(struct archive_entry *);
-__LA_DECL __LA_GID_T	 archive_entry_gid(struct archive_entry *);
+__LA_DECL __LA_INT64_T	 archive_entry_gid(struct archive_entry *);
 __LA_DECL const char	*archive_entry_gname(struct archive_entry *);
 __LA_DECL const wchar_t	*archive_entry_gname_w(struct archive_entry *);
 __LA_DECL const char	*archive_entry_hardlink(struct archive_entry *);
 __LA_DECL const wchar_t	*archive_entry_hardlink_w(struct archive_entry *);
-__LA_DECL __LA_INO_T	 archive_entry_ino(struct archive_entry *);
+__LA_DECL __LA_INT64_T	 archive_entry_ino(struct archive_entry *);
 __LA_DECL __LA_INT64_T	 archive_entry_ino64(struct archive_entry *);
+__LA_DECL int		 archive_entry_ino_is_set(struct archive_entry *);
 __LA_DECL __LA_MODE_T	 archive_entry_mode(struct archive_entry *);
 __LA_DECL time_t	 archive_entry_mtime(struct archive_entry *);
 __LA_DECL long		 archive_entry_mtime_nsec(struct archive_entry *);
@@ -213,35 +229,34 @@
 __LA_DECL unsigned int	 archive_entry_nlink(struct archive_entry *);
 __LA_DECL const char	*archive_entry_pathname(struct archive_entry *);
 __LA_DECL const wchar_t	*archive_entry_pathname_w(struct archive_entry *);
+__LA_DECL __LA_MODE_T	 archive_entry_perm(struct archive_entry *);
 __LA_DECL dev_t		 archive_entry_rdev(struct archive_entry *);
 __LA_DECL dev_t		 archive_entry_rdevmajor(struct archive_entry *);
 __LA_DECL dev_t		 archive_entry_rdevminor(struct archive_entry *);
 __LA_DECL const char	*archive_entry_sourcepath(struct archive_entry *);
+__LA_DECL const wchar_t	*archive_entry_sourcepath_w(struct archive_entry *);
 __LA_DECL __LA_INT64_T	 archive_entry_size(struct archive_entry *);
 __LA_DECL int		 archive_entry_size_is_set(struct archive_entry *);
 __LA_DECL const char	*archive_entry_strmode(struct archive_entry *);
 __LA_DECL const char	*archive_entry_symlink(struct archive_entry *);
 __LA_DECL const wchar_t	*archive_entry_symlink_w(struct archive_entry *);
-__LA_DECL __LA_UID_T	 archive_entry_uid(struct archive_entry *);
+__LA_DECL __LA_INT64_T	 archive_entry_uid(struct archive_entry *);
 __LA_DECL const char	*archive_entry_uname(struct archive_entry *);
 __LA_DECL const wchar_t	*archive_entry_uname_w(struct archive_entry *);
 
 /*
  * Set fields in an archive_entry.
  *
- * Note that string 'set' functions do not copy the string, only the pointer.
- * In contrast, 'copy' functions do copy the object pointed to.
- *
- * Note: As of libarchive 2.4, 'set' functions do copy the string and
- * are therefore exact synonyms for the 'copy' versions.  The 'copy'
- * names will be retired in libarchive 3.0.
+ * Note: Before libarchive 2.4, there were 'set' and 'copy' versions
+ * of the string setters.  'copy' copied the actual string, 'set' just
+ * stored the pointer.  In libarchive 2.4 and later, strings are
+ * always copied.
  */
 
 __LA_DECL void	archive_entry_set_atime(struct archive_entry *, time_t, long);
 __LA_DECL void  archive_entry_unset_atime(struct archive_entry *);
 #if defined(_WIN32) && !defined(__CYGWIN__)
-__LA_DECL void archive_entry_copy_bhfi(struct archive_entry *,
-									   BY_HANDLE_FILE_INFORMATION *);
+__LA_DECL void archive_entry_copy_bhfi(struct archive_entry *, BY_HANDLE_FILE_INFORMATION *);
 #endif
 __LA_DECL void	archive_entry_set_birthtime(struct archive_entry *, time_t, long);
 __LA_DECL void  archive_entry_unset_birthtime(struct archive_entry *);
@@ -259,7 +274,7 @@
 	    const char *);
 __LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
 	    const wchar_t *);
-__LA_DECL void	archive_entry_set_gid(struct archive_entry *, __LA_GID_T);
+__LA_DECL void	archive_entry_set_gid(struct archive_entry *, __LA_INT64_T);
 __LA_DECL void	archive_entry_set_gname(struct archive_entry *, const char *);
 __LA_DECL void	archive_entry_copy_gname(struct archive_entry *, const char *);
 __LA_DECL void	archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
@@ -268,12 +283,7 @@
 __LA_DECL void	archive_entry_copy_hardlink(struct archive_entry *, const char *);
 __LA_DECL void	archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
 __LA_DECL int	archive_entry_update_hardlink_utf8(struct archive_entry *, const char *);
-#if ARCHIVE_VERSION_NUMBER >= 3000000
-/* Starting with libarchive 3.0, this will be synonym for ino64. */
 __LA_DECL void	archive_entry_set_ino(struct archive_entry *, __LA_INT64_T);
-#else
-__LA_DECL void	archive_entry_set_ino(struct archive_entry *, unsigned long);
-#endif
 __LA_DECL void	archive_entry_set_ino64(struct archive_entry *, __LA_INT64_T);
 __LA_DECL void	archive_entry_set_link(struct archive_entry *, const char *);
 __LA_DECL void	archive_entry_copy_link(struct archive_entry *, const char *);
@@ -294,11 +304,12 @@
 __LA_DECL void	archive_entry_set_size(struct archive_entry *, __LA_INT64_T);
 __LA_DECL void	archive_entry_unset_size(struct archive_entry *);
 __LA_DECL void	archive_entry_copy_sourcepath(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *);
 __LA_DECL void	archive_entry_set_symlink(struct archive_entry *, const char *);
 __LA_DECL void	archive_entry_copy_symlink(struct archive_entry *, const char *);
 __LA_DECL void	archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
 __LA_DECL int	archive_entry_update_symlink_utf8(struct archive_entry *, const char *);
-__LA_DECL void	archive_entry_set_uid(struct archive_entry *, __LA_UID_T);
+__LA_DECL void	archive_entry_set_uid(struct archive_entry *, __LA_INT64_T);
 __LA_DECL void	archive_entry_set_uname(struct archive_entry *, const char *);
 __LA_DECL void	archive_entry_copy_uname(struct archive_entry *, const char *);
 __LA_DECL void	archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
@@ -315,6 +326,15 @@
 __LA_DECL const struct stat	*archive_entry_stat(struct archive_entry *);
 __LA_DECL void	archive_entry_copy_stat(struct archive_entry *, const struct stat *);
 
+/*
+ * Storage for Mac OS-specific AppleDouble metadata information.
+ * Apple-format tar files store a separate binary blob containing
+ * encoded metadata with ACL, extended attributes, etc.
+ * This provides a place to store that blob.
+ */
+
+__LA_DECL const void * archive_entry_mac_metadata(struct archive_entry *, size_t *);
+__LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const void *, size_t);
 
 /*
  * ACL routines.  This used to simply store and return text-format ACL
@@ -326,32 +346,95 @@
  *
  *  This last point, in particular, forces me to implement a reasonably
  *  complete set of ACL support routines.
- *
- *  TODO: Extend this to support NFSv4/NTFS permissions.  That should
- *  allow full ACL support on Mac OS, in particular, which uses
- *  POSIX.1e-style interfaces to manipulate NFSv4/NTFS permissions.
  */
 
 /*
- * Permission bits mimic POSIX.1e.  Note that I've not followed POSIX.1e's
- * "permset"/"perm" abstract type nonsense.  A permset is just a simple
- * bitmap, following long-standing Unix tradition.
+ * Permission bits.
  */
-#define	ARCHIVE_ENTRY_ACL_EXECUTE	1
-#define	ARCHIVE_ENTRY_ACL_WRITE		2
-#define	ARCHIVE_ENTRY_ACL_READ		4
+#define	ARCHIVE_ENTRY_ACL_EXECUTE             0x00000001
+#define	ARCHIVE_ENTRY_ACL_WRITE               0x00000002
+#define	ARCHIVE_ENTRY_ACL_READ                0x00000004
+#define	ARCHIVE_ENTRY_ACL_READ_DATA           0x00000008
+#define	ARCHIVE_ENTRY_ACL_LIST_DIRECTORY      0x00000008
+#define	ARCHIVE_ENTRY_ACL_WRITE_DATA          0x00000010
+#define	ARCHIVE_ENTRY_ACL_ADD_FILE            0x00000010
+#define	ARCHIVE_ENTRY_ACL_APPEND_DATA         0x00000020
+#define	ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY    0x00000020
+#define	ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS    0x00000040
+#define	ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS   0x00000080
+#define	ARCHIVE_ENTRY_ACL_DELETE_CHILD        0x00000100
+#define	ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES     0x00000200
+#define	ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES    0x00000400
+#define	ARCHIVE_ENTRY_ACL_DELETE              0x00000800
+#define	ARCHIVE_ENTRY_ACL_READ_ACL            0x00001000
+#define	ARCHIVE_ENTRY_ACL_WRITE_ACL           0x00002000
+#define	ARCHIVE_ENTRY_ACL_WRITE_OWNER         0x00004000
+#define	ARCHIVE_ENTRY_ACL_SYNCHRONIZE         0x00008000
 
-/* We need to be able to specify either or both of these. */
-#define	ARCHIVE_ENTRY_ACL_TYPE_ACCESS	256
-#define	ARCHIVE_ENTRY_ACL_TYPE_DEFAULT	512
+#define	ARCHIVE_ENTRY_ACL_PERMS_POSIX1E			\
+	(ARCHIVE_ENTRY_ACL_EXECUTE			\
+	    | ARCHIVE_ENTRY_ACL_WRITE			\
+	    | ARCHIVE_ENTRY_ACL_READ)
+
+#define ARCHIVE_ENTRY_ACL_PERMS_NFS4			\
+	(ARCHIVE_ENTRY_ACL_EXECUTE			\
+	    | ARCHIVE_ENTRY_ACL_READ_DATA		\
+	    | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY 		\
+	    | ARCHIVE_ENTRY_ACL_WRITE_DATA		\
+	    | ARCHIVE_ENTRY_ACL_ADD_FILE		\
+	    | ARCHIVE_ENTRY_ACL_APPEND_DATA		\
+	    | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY	\
+	    | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS	\
+	    | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS	\
+	    | ARCHIVE_ENTRY_ACL_DELETE_CHILD		\
+	    | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES		\
+	    | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES	\
+	    | ARCHIVE_ENTRY_ACL_DELETE			\
+	    | ARCHIVE_ENTRY_ACL_READ_ACL		\
+	    | ARCHIVE_ENTRY_ACL_WRITE_ACL		\
+	    | ARCHIVE_ENTRY_ACL_WRITE_OWNER		\
+	    | ARCHIVE_ENTRY_ACL_SYNCHRONIZE)
+
+/*
+ * Inheritance values (NFS4 ACLs only); included in permset.
+ */
+#define	ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT                0x02000000
+#define	ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT           0x04000000
+#define	ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT        0x08000000
+#define	ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY                0x10000000
+#define	ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS           0x20000000
+#define	ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS               0x40000000
+
+#define	ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4			\
+	(ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT			\
+	    | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT		\
+	    | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT	\
+	    | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY		\
+	    | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS		\
+	    | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS)
+
+/* We need to be able to specify combinations of these. */
+#define	ARCHIVE_ENTRY_ACL_TYPE_ACCESS	256  /* POSIX.1e only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_DEFAULT	512  /* POSIX.1e only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_ALLOW	1024 /* NFS4 only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_DENY	2048 /* NFS4 only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_AUDIT	4096 /* NFS4 only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_ALARM	8192 /* NFS4 only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_POSIX1E	(ARCHIVE_ENTRY_ACL_TYPE_ACCESS \
+	    | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
+#define	ARCHIVE_ENTRY_ACL_TYPE_NFS4	(ARCHIVE_ENTRY_ACL_TYPE_ALLOW \
+	    | ARCHIVE_ENTRY_ACL_TYPE_DENY \
+	    | ARCHIVE_ENTRY_ACL_TYPE_AUDIT \
+	    | ARCHIVE_ENTRY_ACL_TYPE_ALARM)
 
 /* Tag values mimic POSIX.1e */
 #define	ARCHIVE_ENTRY_ACL_USER		10001	/* Specified user. */
 #define	ARCHIVE_ENTRY_ACL_USER_OBJ 	10002	/* User who owns the file. */
 #define	ARCHIVE_ENTRY_ACL_GROUP		10003	/* Specified group. */
 #define	ARCHIVE_ENTRY_ACL_GROUP_OBJ	10004	/* Group who owns the file. */
-#define	ARCHIVE_ENTRY_ACL_MASK		10005	/* Modify group access. */
-#define	ARCHIVE_ENTRY_ACL_OTHER		10006	/* Public. */
+#define	ARCHIVE_ENTRY_ACL_MASK		10005	/* Modify group access (POSIX.1e only) */
+#define	ARCHIVE_ENTRY_ACL_OTHER		10006	/* Public (POSIX.1e only) */
+#define	ARCHIVE_ENTRY_ACL_EVERYONE	10107   /* Everyone (NFS4 only) */
 
 /*
  * Set the ACL by clearing it and adding entries one at a time.
@@ -363,17 +446,17 @@
  * default and access information in a single ACL list.
  */
 __LA_DECL void	 archive_entry_acl_clear(struct archive_entry *);
-__LA_DECL void	 archive_entry_acl_add_entry(struct archive_entry *,
+__LA_DECL int	 archive_entry_acl_add_entry(struct archive_entry *,
 	    int /* type */, int /* permset */, int /* tag */,
 	    int /* qual */, const char * /* name */);
-__LA_DECL void	 archive_entry_acl_add_entry_w(struct archive_entry *,
+__LA_DECL int	 archive_entry_acl_add_entry_w(struct archive_entry *,
 	    int /* type */, int /* permset */, int /* tag */,
 	    int /* qual */, const wchar_t * /* name */);
 
 /*
  * To retrieve the ACL, first "reset", then repeatedly ask for the
  * "next" entry.  The want_type parameter allows you to request only
- * access entries or only default entries.
+ * certain types of entries.
  */
 __LA_DECL int	 archive_entry_acl_reset(struct archive_entry *, int /* want_type */);
 __LA_DECL int	 archive_entry_acl_next(struct archive_entry *, int /* want_type */,
@@ -387,36 +470,29 @@
  * Construct a text-format ACL.  The flags argument is a bitmask that
  * can include any of the following:
  *
- * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include access entries.
- * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include default entries.
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e "access" entries.
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e "default" entries.
+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - Include NFS4 entries.
  * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
- *    each ACL entry.  (As used by 'star'.)
+ *    each ACL entry.  ('star' introduced this for POSIX.1e, this flag
+ *    also applies to NFS4.)
  * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each
- *    default ACL entry.
+ *    default ACL entry, as used in old Solaris ACLs.
  */
 #define	ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID	1024
 #define	ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT	2048
 __LA_DECL const wchar_t	*archive_entry_acl_text_w(struct archive_entry *,
 		    int /* flags */);
+__LA_DECL const char *archive_entry_acl_text(struct archive_entry *,
+		    int /* flags */);
 
 /* Return a count of entries matching 'want_type' */
 __LA_DECL int	 archive_entry_acl_count(struct archive_entry *, int /* want_type */);
 
-/*
- * Private ACL parser.  This is private because it handles some
- * very weird formats that clients should not be messing with.
- * Clients should only deal with their platform-native formats.
- * Because of the need to support many formats cleanly, new arguments
- * are likely to get added on a regular basis.  Clients who try to use
- * this interface are likely to be surprised when it changes.
- *
- * You were warned!
- *
- * TODO: Move this declaration out of the public header and into
- * a private header.  Warnings above are silly.
- */
-__LA_DECL int		 __archive_entry_acl_parse_w(struct archive_entry *,
-		    const wchar_t *, int /* type */);
+/* Return an opaque ACL object. */
+/* There's not yet anything clients can actually do with this... */
+struct archive_acl;
+__LA_DECL struct archive_acl *archive_entry_acl(struct archive_entry *);
 
 /*
  * extended attributes
@@ -438,6 +514,24 @@
 	    const char ** /* name */, const void ** /* value */, size_t *);
 
 /*
+ * sparse
+ */
+
+__LA_DECL void	 archive_entry_sparse_clear(struct archive_entry *);
+__LA_DECL void	 archive_entry_sparse_add_entry(struct archive_entry *,
+	    __LA_INT64_T /* offset */, __LA_INT64_T /* length */);
+
+/*
+ * To retrieve the xattr list, first "reset", then repeatedly ask for the
+ * "next" entry.
+ */
+
+__LA_DECL int	archive_entry_sparse_count(struct archive_entry *);
+__LA_DECL int	archive_entry_sparse_reset(struct archive_entry *);
+__LA_DECL int	archive_entry_sparse_next(struct archive_entry *,
+	    __LA_INT64_T * /* offset */, __LA_INT64_T * /* length */);
+
+/*
  * Utility to match up hardlinks.
  *
  * The 'struct archive_entry_linkresolver' is a cache of archive entries
@@ -449,7 +543,7 @@
  *      be written.
  *   4. Call archive_entry_linkify(resolver, NULL) until
  *      no more entries are returned.
- *   5. Call archive_entry_link_resolver_free(resolver) to free resources.
+ *   5. Call archive_entry_linkresolver_free(resolver) to free resources.
  *
  * The entries returned have their hardlink and size fields updated
  * appropriately.  If an entry is passed in that does not refer to
@@ -499,7 +593,7 @@
  *           linkify(l2) => l1
  *           linkify(NULL) => l2   (at end, you retrieve remaining links)
  *    As the name suggests, this strategy is used by newer cpio variants.
- *    It's noticably more complex for the archiver, slightly more complex
+ *    It's noticeably more complex for the archiver, slightly more complex
  *    for the dearchiver than the tar strategy, but makes it straightforward
  *    to restore a file using any link by simply continuing to scan until
  *    you see a link that is stored with a body.  In contrast, the tar
@@ -513,6 +607,8 @@
 __LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);
 __LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *,
     struct archive_entry **, struct archive_entry **);
+__LA_DECL struct archive_entry *archive_entry_partial_links(
+    struct archive_entry_linkresolver *res, unsigned int *links);
 
 #ifdef __cplusplus
 }
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_entry_copy_stat.c
--- a/head/contrib/libarchive/libarchive/archive_entry_copy_stat.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_entry_copy_stat.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,12 +24,13 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_entry_copy_stat.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_entry_copy_stat.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
 
+#include "archive.h"
 #include "archive_entry.h"
 
 void
@@ -59,12 +60,13 @@
 	archive_entry_set_atime(entry, st->st_atime, 0);
 	archive_entry_set_ctime(entry, st->st_ctime, 0);
 	archive_entry_set_mtime(entry, st->st_mtime, 0);
-#if HAVE_STRUCT_STAT_ST_BIRTHTIME
-	archive_entry_set_birthtime(entry, st->st_birthtime, 0);
-#endif
 #endif
 #if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
 	archive_entry_set_birthtime(entry, st->st_birthtime, st->st_birthtimespec.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_BIRTHTIME
+	archive_entry_set_birthtime(entry, st->st_birthtime, 0);
+#else
+	archive_entry_unset_birthtime(entry);
 #endif
 	archive_entry_set_dev(entry, st->st_dev);
 	archive_entry_set_gid(entry, st->st_gid);
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_entry_link_resolver.c
--- a/head/contrib/libarchive/libarchive/archive_entry_link_resolver.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_entry_link_resolver.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_entry_link_resolver.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_entry_link_resolver.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -70,10 +70,10 @@
 struct links_entry {
 	struct links_entry	*next;
 	struct links_entry	*previous;
-	int			 links; /* # links not yet seen */
-	int			 hash;
 	struct archive_entry	*canonical;
 	struct archive_entry	*entry;
+	size_t			 hash;
+	unsigned int		 links; /* # links not yet seen */
 };
 
 struct archive_entry_linkresolver {
@@ -84,32 +84,37 @@
 	int			  strategy;
 };
 
+#define	NEXT_ENTRY_DEFERRED	1
+#define	NEXT_ENTRY_PARTIAL	2
+#define	NEXT_ENTRY_ALL		(NEXT_ENTRY_DEFERRED | NEXT_ENTRY_PARTIAL)
+
 static struct links_entry *find_entry(struct archive_entry_linkresolver *,
 		    struct archive_entry *);
 static void grow_hash(struct archive_entry_linkresolver *);
 static struct links_entry *insert_entry(struct archive_entry_linkresolver *,
 		    struct archive_entry *);
-static struct links_entry *next_entry(struct archive_entry_linkresolver *);
+static struct links_entry *next_entry(struct archive_entry_linkresolver *,
+    int);
 
 struct archive_entry_linkresolver *
 archive_entry_linkresolver_new(void)
 {
 	struct archive_entry_linkresolver *res;
-	size_t i;
 
-	res = malloc(sizeof(struct archive_entry_linkresolver));
+	/* Check for positive power-of-two */
+	if (links_cache_initial_size == 0 ||
+	    (links_cache_initial_size & (links_cache_initial_size - 1)) != 0)
+		return (NULL);
+
+	res = calloc(1, sizeof(struct archive_entry_linkresolver));
 	if (res == NULL)
 		return (NULL);
-	memset(res, 0, sizeof(struct archive_entry_linkresolver));
 	res->number_buckets = links_cache_initial_size;
-	res->buckets = malloc(res->number_buckets *
-	    sizeof(res->buckets[0]));
+	res->buckets = calloc(res->number_buckets, sizeof(res->buckets[0]));
 	if (res->buckets == NULL) {
 		free(res);
 		return (NULL);
 	}
-	for (i = 0; i < res->number_buckets; i++)
-		res->buckets[i] = NULL;
 	return (res);
 }
 
@@ -120,6 +125,11 @@
 	int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK;
 
 	switch (fmtbase) {
+	case ARCHIVE_FORMAT_7ZIP:
+	case ARCHIVE_FORMAT_AR:
+	case ARCHIVE_FORMAT_ZIP:
+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
+		break;
 	case ARCHIVE_FORMAT_CPIO:
 		switch (fmt) {
 		case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC:
@@ -134,11 +144,14 @@
 	case ARCHIVE_FORMAT_MTREE:
 		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE;
 		break;
+	case ARCHIVE_FORMAT_ISO9660:
+	case ARCHIVE_FORMAT_SHAR:
 	case ARCHIVE_FORMAT_TAR:
+	case ARCHIVE_FORMAT_XAR:
 		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
 		break;
 	default:
-		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
 		break;
 	}
 }
@@ -151,12 +164,9 @@
 	if (res == NULL)
 		return;
 
-	if (res->buckets != NULL) {
-		while ((le = next_entry(res)) != NULL)
-			archive_entry_free(le->entry);
-		free(res->buckets);
-		res->buckets = NULL;
-	}
+	while ((le = next_entry(res, NEXT_ENTRY_ALL)) != NULL)
+		archive_entry_free(le->entry);
+	free(res->buckets);
 	free(res);
 }
 
@@ -170,7 +180,7 @@
 	*f = NULL; /* Default: Don't return a second entry. */
 
 	if (*e == NULL) {
-		le = next_entry(res);
+		le = next_entry(res, NEXT_ENTRY_DEFERRED);
 		if (le != NULL) {
 			*e = le->entry;
 			le->entry = NULL;
@@ -249,7 +259,7 @@
     struct archive_entry *entry)
 {
 	struct links_entry	*le;
-	int			 hash, bucket;
+	size_t			 hash, bucket;
 	dev_t			 dev;
 	int64_t			 ino;
 
@@ -261,16 +271,12 @@
 		res->spare = NULL;
 	}
 
-	/* If the links cache overflowed and got flushed, don't bother. */
-	if (res->buckets == NULL)
-		return (NULL);
-
 	dev = archive_entry_dev(entry);
 	ino = archive_entry_ino64(entry);
-	hash = (int)(dev ^ ino);
+	hash = (size_t)(dev ^ ino);
 
 	/* Try to locate this entry in the links cache. */
-	bucket = hash % res->number_buckets;
+	bucket = hash & (res->number_buckets - 1);
 	for (le = res->buckets[bucket]; le != NULL; le = le->next) {
 		if (le->hash == hash
 		    && dev == archive_entry_dev(le->canonical)
@@ -301,7 +307,7 @@
 }
 
 static struct links_entry *
-next_entry(struct archive_entry_linkresolver *res)
+next_entry(struct archive_entry_linkresolver *res, int mode)
 {
 	struct links_entry	*le;
 	size_t			 bucket;
@@ -309,22 +315,27 @@
 	/* Free a held entry. */
 	if (res->spare != NULL) {
 		archive_entry_free(res->spare->canonical);
+		archive_entry_free(res->spare->entry);
 		free(res->spare);
 		res->spare = NULL;
 	}
 
-	/* If the links cache overflowed and got flushed, don't bother. */
-	if (res->buckets == NULL)
-		return (NULL);
-
 	/* Look for next non-empty bucket in the links cache. */
 	for (bucket = 0; bucket < res->number_buckets; bucket++) {
-		le = res->buckets[bucket];
-		if (le != NULL) {
+		for (le = res->buckets[bucket]; le != NULL; le = le->next) {
+			if (le->entry != NULL &&
+			    (mode & NEXT_ENTRY_DEFERRED) == 0)
+				continue;
+			if (le->entry == NULL &&
+			    (mode & NEXT_ENTRY_PARTIAL) == 0)
+				continue;
 			/* Remove it from this hash bucket. */
 			if (le->next != NULL)
 				le->next->previous = le->previous;
-			res->buckets[bucket] = le->next;
+			if (le->previous != NULL)
+				le->previous->next = le->next;
+			else
+				res->buckets[bucket] = le->next;
 			res->number_entries--;
 			/* Defer freeing this entry. */
 			res->spare = le;
@@ -339,13 +350,12 @@
     struct archive_entry *entry)
 {
 	struct links_entry *le;
-	int			 hash, bucket;
+	size_t hash, bucket;
 
 	/* Add this entry to the links cache. */
-	le = malloc(sizeof(struct links_entry));
+	le = calloc(1, sizeof(struct links_entry));
 	if (le == NULL)
 		return (NULL);
-	memset(le, 0, sizeof(*le));
 	le->canonical = archive_entry_clone(entry);
 
 	/* If the links cache is getting too full, enlarge the hash table. */
@@ -353,7 +363,7 @@
 		grow_hash(res);
 
 	hash = archive_entry_dev(entry) ^ archive_entry_ino64(entry);
-	bucket = hash % res->number_buckets;
+	bucket = hash & (res->number_buckets - 1);
 
 	/* If we could allocate the entry, record it. */
 	if (res->buckets[bucket] != NULL)
@@ -376,30 +386,59 @@
 
 	/* Try to enlarge the bucket list. */
 	new_size = res->number_buckets * 2;
-	new_buckets = malloc(new_size * sizeof(struct links_entry *));
+	if (new_size < res->number_buckets)
+		return;
+	new_buckets = calloc(new_size, sizeof(struct links_entry *));
 
-	if (new_buckets != NULL) {
-		memset(new_buckets, 0,
-		    new_size * sizeof(struct links_entry *));
-		for (i = 0; i < res->number_buckets; i++) {
-			while (res->buckets[i] != NULL) {
-				/* Remove entry from old bucket. */
-				le = res->buckets[i];
-				res->buckets[i] = le->next;
+	if (new_buckets == NULL)
+		return;
 
-				/* Add entry to new bucket. */
-				bucket = le->hash % new_size;
+	for (i = 0; i < res->number_buckets; i++) {
+		while (res->buckets[i] != NULL) {
+			/* Remove entry from old bucket. */
+			le = res->buckets[i];
+			res->buckets[i] = le->next;
 
-				if (new_buckets[bucket] != NULL)
-					new_buckets[bucket]->previous =
-					    le;
-				le->next = new_buckets[bucket];
-				le->previous = NULL;
-				new_buckets[bucket] = le;
-			}
+			/* Add entry to new bucket. */
+			bucket = le->hash & (new_size - 1);
+
+			if (new_buckets[bucket] != NULL)
+				new_buckets[bucket]->previous = le;
+			le->next = new_buckets[bucket];
+			le->previous = NULL;
+			new_buckets[bucket] = le;
 		}
-		free(res->buckets);
-		res->buckets = new_buckets;
-		res->number_buckets = new_size;
 	}
+	free(res->buckets);
+	res->buckets = new_buckets;
+	res->number_buckets = new_size;
 }
+
+struct archive_entry *
+archive_entry_partial_links(struct archive_entry_linkresolver *res,
+    unsigned int *links)
+{
+	struct archive_entry	*e;
+	struct links_entry	*le;
+
+	/* Free a held entry. */
+	if (res->spare != NULL) {
+		archive_entry_free(res->spare->canonical);
+		archive_entry_free(res->spare->entry);
+		free(res->spare);
+		res->spare = NULL;
+	}
+
+	le = next_entry(res, NEXT_ENTRY_PARTIAL);
+	if (le != NULL) {
+		e = le->canonical;
+		if (links != NULL)
+			*links = le->links;
+		le->canonical = NULL;
+	} else {
+		e = NULL;
+		if (links != NULL)
+			*links = 0;
+	}
+	return (e);
+}
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_entry_private.h
--- a/head/contrib/libarchive/libarchive/archive_entry_private.h	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_entry_private.h	Fri Mar 02 16:54:40 2012 +0200
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/contrib/libarchive/libarchive/archive_entry_private.h 228763 2011-12-21 11:13:29Z mm $
+ * $FreeBSD: head/contrib/libarchive/libarchive/archive_entry_private.h 232153 2012-02-25 10:58:02Z mm $
  */
 
 #ifndef __LIBARCHIVE_BUILD
@@ -32,36 +32,9 @@
 #ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
 #define	ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
 
+#include "archive_acl_private.h"
 #include "archive_string.h"
 
-/*
- * Handle wide character (i.e., Unicode) and non-wide character
- * strings transparently.
- */
-
-struct aes {
-	struct archive_string aes_mbs;
-	struct archive_string aes_utf8;
-	const wchar_t *aes_wcs;
-	/* Bitmap of which of the above are valid.  Because we're lazy
-	 * about malloc-ing and reusing the underlying storage, we
-	 * can't rely on NULL pointers to indicate whether a string
-	 * has been set. */
-	int aes_set;
-#define	AES_SET_MBS 1
-#define	AES_SET_UTF8 2
-#define	AES_SET_WCS 4
-};
-
-struct ae_acl {
-	struct ae_acl *next;
-	int	type;			/* E.g., access or default */
-	int	tag;			/* E.g., user/group/other/mask */
-	int	permset;		/* r/w/x bits */
-	int	id;			/* uid/gid for user/group */
-	struct aes name;		/* uname/gname */
-};
-
 struct ae_xattr {
 	struct ae_xattr *next;
 
@@ -70,6 +43,13 @@
 	size_t	size;
 };
 
+struct ae_sparse {
+	struct ae_sparse *next;
+
+	int64_t	 offset;
+	int64_t	 length;
+};
+
 /*
  * Description of an archive entry.
  *
@@ -91,6 +71,8 @@
  * TODO: Design a good API for handling sparse files.
  */
 struct archive_entry {
+	struct archive *archive;
+
 	/*
 	 * Note that ae_stat.st_mode & AE_IFMT  can be  0!
 	 *
@@ -101,10 +83,15 @@
 	 */
 
 	/*
-	 * Read archive_entry_copy_stat.c for an explanation of why I
-	 * don't just use "struct stat" instead of "struct aest" here
-	 * and why I have this odd pointer to a separately-allocated
-	 * struct stat.
+	 * We have a "struct aest" for holding file metadata rather than just
+	 * a "struct stat" because on some platforms the "struct stat" has
+	 * fields which are too narrow to hold the range of possible values;
+	 * we don't want to lose information if we read an archive and write
+	 * out another (e.g., in "tar -cf new.tar @old.tar").
+	 *
+	 * The "stat" pointer points to some form of platform-specific struct
+	 * stat; it is declared as a void * rather than a struct stat * as
+	 * some platforms have multiple varieties of stat structures.
 	 */
 	void *stat;
 	int  stat_valid; /* Set to 0 whenever a field in aest changes. */
@@ -118,12 +105,11 @@
 		uint32_t	aest_mtime_nsec;
 		int64_t		aest_birthtime;
 		uint32_t	aest_birthtime_nsec;
-		gid_t		aest_gid;
+		int64_t		aest_gid;
 		int64_t		aest_ino;
-		mode_t		aest_mode;
 		uint32_t	aest_nlink;
 		uint64_t	aest_size;
-		uid_t		aest_uid;
+		int64_t		aest_uid;
 		/*
 		 * Because converting between device codes and
 		 * major/minor values is platform-specific and
@@ -150,35 +136,41 @@
 #define	AE_SET_MTIME	16
 #define	AE_SET_BIRTHTIME 32
 #define	AE_SET_SIZE	64
+#define	AE_SET_INO	128
+#define	AE_SET_DEV	256
 
 	/*
 	 * Use aes here so that we get transparent mbs<->wcs conversions.
 	 */
-	struct aes ae_fflags_text;	/* Text fflags per fflagstostr(3) */
+	struct archive_mstring ae_fflags_text;	/* Text fflags per fflagstostr(3) */
 	unsigned long ae_fflags_set;		/* Bitmap fflags */
 	unsigned long ae_fflags_clear;
-	struct aes ae_gname;		/* Name of owning group */
-	struct aes ae_hardlink;	/* Name of target for hardlink */
-	struct aes ae_pathname;	/* Name of entry */
-	struct aes ae_symlink;		/* symlink contents */
-	struct aes ae_uname;		/* Name of owner */
+	struct archive_mstring ae_gname;		/* Name of owning group */
+	struct archive_mstring ae_hardlink;	/* Name of target for hardlink */
+	struct archive_mstring ae_pathname;	/* Name of entry */
+	struct archive_mstring ae_symlink;		/* symlink contents */
+	struct archive_mstring ae_uname;		/* Name of owner */
 
 	/* Not used within libarchive; useful for some clients. */
-	struct aes ae_sourcepath;	/* Path this entry is sourced from. */
+	struct archive_mstring ae_sourcepath;	/* Path this entry is sourced from. */
+
+	void *mac_metadata;
+	size_t mac_metadata_size;
 
 	/* ACL support. */
-	struct ae_acl	*acl_head;
-	struct ae_acl	*acl_p;
-	int		 acl_state;	/* See acl_next for details. */
-	wchar_t		*acl_text_w;
+	struct archive_acl    acl;
 
 	/* extattr support. */
 	struct ae_xattr *xattr_head;
 	struct ae_xattr *xattr_p;
 
+	/* sparse support. */
+	struct ae_sparse *sparse_head;
+	struct ae_sparse *sparse_tail;
+	struct ae_sparse *sparse_p;
+
 	/* Miscellaneous. */
 	char		 strmode[12];
 };
 
-
 #endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_entry_stat.c
--- a/head/contrib/libarchive/libarchive/archive_entry_stat.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_entry_stat.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_entry_stat.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_entry_stat.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -41,7 +41,7 @@
 {
 	struct stat *st;
 	if (entry->stat == NULL) {
-		entry->stat = malloc(sizeof(*st));
+		entry->stat = calloc(1, sizeof(*st));
 		if (entry->stat == NULL)
 			return (NULL);
 		entry->stat_valid = 0;
@@ -110,7 +110,7 @@
 	/*
 	 * TODO: On Linux, store 32 or 64 here depending on whether
 	 * the cached stat structure is a stat32 or a stat64.  This
-	 * will allow us to support both variants interchangably.
+	 * will allow us to support both variants interchangeably.
 	 */
 	entry->stat_valid = 1;
 
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_private.h
--- a/head/contrib/libarchive/libarchive/archive_private.h	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_private.h	Fri Mar 02 16:54:40 2012 +0200
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/contrib/libarchive/libarchive/archive_private.h 228773 2011-12-21 15:18:52Z mm $
+ * $FreeBSD: head/contrib/libarchive/libarchive/archive_private.h 232153 2012-02-25 10:58:02Z mm $
  */
 
 #ifndef __LIBARCHIVE_BUILD
@@ -32,6 +32,10 @@
 #ifndef ARCHIVE_PRIVATE_H_INCLUDED
 #define	ARCHIVE_PRIVATE_H_INCLUDED
 
+#if HAVE_ICONV_H
+#include <iconv.h>
+#endif
+
 #include "archive.h"
 #include "archive_string.h"
 
@@ -47,14 +51,13 @@
 #define	ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U)
 #define	ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U)
 
-#define	ARCHIVE_STATE_ANY	0xFFFFU
 #define	ARCHIVE_STATE_NEW	1U
 #define	ARCHIVE_STATE_HEADER	2U
 #define	ARCHIVE_STATE_DATA	4U
-#define	ARCHIVE_STATE_DATA_END	8U
 #define	ARCHIVE_STATE_EOF	0x10U
 #define	ARCHIVE_STATE_CLOSED	0x20U
 #define	ARCHIVE_STATE_FATAL	0x8000U
+#define	ARCHIVE_STATE_ANY	(0xFFFFU & ~ARCHIVE_STATE_FATAL)
 
 struct archive_vtable {
 	int	(*archive_close)(struct archive *);
@@ -65,9 +68,23 @@
 	ssize_t	(*archive_write_data)(struct archive *,
 	    const void *, size_t);
 	ssize_t	(*archive_write_data_block)(struct archive *,
-	    const void *, size_t, off_t);
+	    const void *, size_t, int64_t);
+
+	int	(*archive_read_next_header)(struct archive *,
+	    struct archive_entry **);
+	int	(*archive_read_next_header2)(struct archive *,
+	    struct archive_entry *);
+	int	(*archive_read_data_block)(struct archive *,
+	    const void **, size_t *, int64_t *);
+
+	int	(*archive_filter_count)(struct archive *);
+	int64_t (*archive_filter_bytes)(struct archive *, int);
+	int	(*archive_filter_code)(struct archive *, int);
+	const char * (*archive_filter_name)(struct archive *, int);
 };
 
+struct archive_string_conv;
+
 struct archive {
 	/*
 	 * The magic/state values are used to sanity-check the
@@ -90,26 +107,35 @@
 	int	  compression_code;	/* Currently active compression. */
 	const char *compression_name;
 
-	/* Position in UNCOMPRESSED data stream. */
-	int64_t		  file_position;
-	/* Position in COMPRESSED data stream. */
-	int64_t		  raw_position;
 	/* Number of file entries processed. */
 	int		  file_count;
 
 	int		  archive_error_number;
 	const char	 *error;
 	struct archive_string	error_string;
+
+	char *current_code;
+	unsigned current_codepage; /* Current ACP(ANSI CodePage). */
+	unsigned current_oemcp; /* Current OEMCP(OEM CodePage). */
+	struct archive_string_conv *sconv;
 };
 
-/* Check magic value and state; exit if it isn't valid. */
-void	__archive_check_magic(struct archive *, unsigned int magic,
+/* Check magic value and state; return(ARCHIVE_FATAL) if it isn't valid. */
+int	__archive_check_magic(struct archive *, unsigned int magic,
 	    unsigned int state, const char *func);
+#define	archive_check_magic(a, expected_magic, allowed_states, function_name) \
+	do { \
+		int magic_test = __archive_check_magic((a), (expected_magic), \
+			(allowed_states), (function_name)); \
+		if (magic_test == ARCHIVE_FATAL) \
+			return ARCHIVE_FATAL; \
+	} while (0)
 
 void	__archive_errx(int retvalue, const char *msg) __LA_DEAD;
 
-int	__archive_parse_options(const char *p, const char *fn,
-	    int keysize, char *key, int valsize, char *val);
+int	__archive_mktemp(const char *tmpdir);
+
+int	__archive_clean(struct archive *);
 
 #define	err_combine(a,b)	((a) < (b) ? (a) : (b))
 
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read.3
--- a/head/contrib/libarchive/libarchive/archive_read.3	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read.3	Fri Mar 02 16:54:40 2012 +0200
@@ -22,181 +22,16 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: head/contrib/libarchive/libarchive/archive_read.3 228773 2011-12-21 15:18:52Z mm $
+.\" $FreeBSD: head/contrib/libarchive/libarchive/archive_read.3 232153 2012-02-25 10:58:02Z mm $
 .\"
-.Dd April 13, 2009
+.Dd March 23, 2011
 .Dt ARCHIVE_READ 3
 .Os
 .Sh NAME
-.Nm archive_read_new ,
-.Nm archive_read_set_filter_options ,
-.Nm archive_read_set_format_options ,
-.Nm archive_read_set_options ,
-.Nm archive_read_support_compression_all ,
-.Nm archive_read_support_compression_bzip2 ,
-.Nm archive_read_support_compression_compress ,
-.Nm archive_read_support_compression_gzip ,
-.Nm archive_read_support_compression_lzma ,
-.Nm archive_read_support_compression_none ,
-.Nm archive_read_support_compression_xz ,
-.Nm archive_read_support_compression_program ,
-.Nm archive_read_support_compression_program_signature ,
-.Nm archive_read_support_format_all ,
-.Nm archive_read_support_format_ar ,
-.Nm archive_read_support_format_cpio ,
-.Nm archive_read_support_format_empty ,
-.Nm archive_read_support_format_iso9660 ,
-.Nm archive_read_support_format_mtree,
-.Nm archive_read_support_format_raw,
-.Nm archive_read_support_format_tar ,
-.Nm archive_read_support_format_zip ,
-.Nm archive_read_open ,
-.Nm archive_read_open2 ,
-.Nm archive_read_open_fd ,
-.Nm archive_read_open_FILE ,
-.Nm archive_read_open_filename ,
-.Nm archive_read_open_memory ,
-.Nm archive_read_next_header ,
-.Nm archive_read_next_header2 ,
-.Nm archive_read_data ,
-.Nm archive_read_data_block ,
-.Nm archive_read_data_skip ,
-.\" #if ARCHIVE_API_VERSION < 3
-.Nm archive_read_data_into_buffer ,
-.\" #endif
-.Nm archive_read_data_into_fd ,
-.Nm archive_read_extract ,
-.Nm archive_read_extract2 ,
-.Nm archive_read_extract_set_progress_callback ,
-.Nm archive_read_close ,
-.Nm archive_read_free
+.Nm archive_read
 .Nd functions for reading streaming archives
 .Sh SYNOPSIS
 .In archive.h
-.Ft struct archive *
-.Fn archive_read_new "void"
-.Ft int
-.Fn archive_read_support_compression_all "struct archive *"
-.Ft int
-.Fn archive_read_support_compression_bzip2 "struct archive *"
-.Ft int
-.Fn archive_read_support_compression_compress "struct archive *"
-.Ft int
-.Fn archive_read_support_compression_gzip "struct archive *"
-.Ft int
-.Fn archive_read_support_compression_lzma "struct archive *"
-.Ft int
-.Fn archive_read_support_compression_none "struct archive *"
-.Ft int
-.Fn archive_read_support_compression_xz "struct archive *"
-.Ft int
-.Fo archive_read_support_compression_program
-.Fa "struct archive *"
-.Fa "const char *cmd"
-.Fc
-.Ft int
-.Fo archive_read_support_compression_program_signature
-.Fa "struct archive *"
-.Fa "const char *cmd"
-.Fa "const void *signature"
-.Fa "size_t signature_length"
-.Fc
-.Ft int
-.Fn archive_read_support_format_all "struct archive *"
-.Ft int
-.Fn archive_read_support_format_ar "struct archive *"
-.Ft int
-.Fn archive_read_support_format_cpio "struct archive *"
-.Ft int
-.Fn archive_read_support_format_empty "struct archive *"
-.Ft int
-.Fn archive_read_support_format_iso9660 "struct archive *"
-.Ft int
-.Fn archive_read_support_format_mtree "struct archive *"
-.Ft int
-.Fn archive_read_support_format_raw "struct archive *"
-.Ft int
-.Fn archive_read_support_format_tar "struct archive *"
-.Ft int
-.Fn archive_read_support_format_zip "struct archive *"
-.Ft int
-.Fn archive_read_set_filter_options "struct archive *" "const char *"
-.Ft int
-.Fn archive_read_set_format_options "struct archive *" "const char *"
-.Ft int
-.Fn archive_read_set_options "struct archive *" "const char *"
-.Ft int
-.Fo archive_read_open
-.Fa "struct archive *"
-.Fa "void *client_data"
-.Fa "archive_open_callback *"
-.Fa "archive_read_callback *"
-.Fa "archive_close_callback *"
-.Fc
-.Ft int
-.Fo archive_read_open2
-.Fa "struct archive *"
-.Fa "void *client_data"
-.Fa "archive_open_callback *"
-.Fa "archive_read_callback *"
-.Fa "archive_skip_callback *"
-.Fa "archive_close_callback *"
-.Fc
-.Ft int
-.Fn archive_read_open_FILE "struct archive *" "FILE *file"
-.Ft int
-.Fn archive_read_open_fd "struct archive *" "int fd" "size_t block_size"
-.Ft int
-.Fo archive_read_open_filename
-.Fa "struct archive *"
-.Fa "const char *filename"
-.Fa "size_t block_size"
-.Fc
-.Ft int
-.Fn archive_read_open_memory "struct archive *" "void *buff" "size_t size"
-.Ft int
-.Fn archive_read_next_header "struct archive *" "struct archive_entry **"
-.Ft int
-.Fn archive_read_next_header2 "struct archive *" "struct archive_entry *"
-.Ft ssize_t
-.Fn archive_read_data "struct archive *" "void *buff" "size_t len"
-.Ft int
-.Fo archive_read_data_block
-.Fa "struct archive *"
-.Fa "const void **buff"
-.Fa "size_t *len"
-.Fa "off_t *offset"
-.Fc
-.Ft int
-.Fn archive_read_data_skip "struct archive *"
-.\" #if ARCHIVE_API_VERSION < 3
-.Ft int
-.Fn archive_read_data_into_buffer "struct archive *" "void *" "ssize_t len"
-.\" #endif
-.Ft int
-.Fn archive_read_data_into_fd "struct archive *" "int fd"
-.Ft int
-.Fo archive_read_extract
-.Fa "struct archive *"
-.Fa "struct archive_entry *"
-.Fa "int flags"
-.Fc
-.Ft int
-.Fo archive_read_extract2
-.Fa "struct archive *src"
-.Fa "struct archive_entry *"
-.Fa "struct archive *dest"
-.Fc
-.Ft void
-.Fo archive_read_extract_set_progress_callback
-.Fa "struct archive *"
-.Fa "void (*func)(void *)"
-.Fa "void *user_data"
-.Fc
-.Ft int
-.Fn archive_read_close "struct archive *"
-.Ft int
-.Fn archive_read_free "struct archive *"
 .Sh DESCRIPTION
 These functions provide a complete API for reading streaming archives.
 The general process is to first create the
@@ -204,375 +39,120 @@
 object, set options, initialize the reader, iterate over the archive
 headers and associated data, then close the archive and release all
 resources.
-The following summary describes the functions in approximately the
-order they would be used:
-.Bl -tag -compact -width indent
-.It Fn archive_read_new
-Allocates and initializes a
+.\"
+.Ss Create archive object
+See
+.Xr archive_read_new 3 .
+.Pp
+To read an archive, you must first obtain an initialized
 .Tn struct archive
-object suitable for reading from an archive.
-.It Xo
-.Fn archive_read_support_compression_bzip2 ,
-.Fn archive_read_support_compression_compress ,
-.Fn archive_read_support_compression_gzip ,
-.Fn archive_read_support_compression_lzma ,
-.Fn archive_read_support_compression_none ,
-.Fn archive_read_support_compression_xz
-.Xc
-Enables auto-detection code and decompression support for the
-specified compression.
-Returns
-.Cm ARCHIVE_OK
-if the compression is fully supported, or
-.Cm ARCHIVE_WARN
-if the compression is supported only through an external program.
-Note that decompression using an external program is usually slower than
-decompression through built-in libraries.
-Note that
-.Dq none
-is always enabled by default.
-.It Fn archive_read_support_compression_all
-Enables all available decompression filters.
-.It Fn archive_read_support_compression_program
-Data is fed through the specified external program before being dearchived.
-Note that this disables automatic detection of the compression format,
-so it makes no sense to specify this in conjunction with any other
-decompression option.
-.It Fn archive_read_support_compression_program_signature
-This feeds data through the specified external program
-but only if the initial bytes of the data match the specified
-signature value.
-.It Xo
-.Fn archive_read_support_format_all ,
-.Fn archive_read_support_format_ar ,
-.Fn archive_read_support_format_cpio ,
-.Fn archive_read_support_format_empty ,
-.Fn archive_read_support_format_iso9660 ,
-.Fn archive_read_support_format_mtree ,
-.Fn archive_read_support_format_tar ,
-.Fn archive_read_support_format_zip
-.Xc
-Enables support---including auto-detection code---for the
-specified archive format.
-For example,
-.Fn archive_read_support_format_tar
-enables support for a variety of standard tar formats, old-style tar,
-ustar, pax interchange format, and many common variants.
-For convenience,
+object from
+.Fn archive_read_new .
+.\"
+.Ss Enable filters and formats
+See
+.Xr archive_read_filter 3
+and
+.Xr archive_read_format 3 .
+.Pp
+You can then modify this object for the desired operations with the
+various
+.Fn archive_read_set_XXX
+and
+.Fn archive_read_support_XXX
+functions.
+In particular, you will need to invoke appropriate
+.Fn archive_read_support_XXX
+functions to enable the corresponding compression and format
+support.
+Note that these latter functions perform two distinct operations:
+they cause the corresponding support code to be linked into your
+program, and they enable the corresponding auto-detect code.
+Unless you have specific constraints, you will generally want
+to invoke
+.Fn archive_read_support_filter_all
+and
 .Fn archive_read_support_format_all
-enables support for all available formats.
-Only empty archives are supported by default.
-.It Fn archive_read_support_format_raw
-The
-.Dq raw
-format handler allows libarchive to be used to read arbitrary data.
-It treats any data stream as an archive with a single entry.
-The pathname of this entry is
-.Dq data ;
-all other entry fields are unset.
-This is not enabled by
-.Fn archive_read_support_format_all
-in order to avoid erroneous handling of damaged archives.
-.It Xo
-.Fn archive_read_set_filter_options ,
-.Fn archive_read_set_format_options ,
-.Fn archive_read_set_options
-.Xc
-Specifies options that will be passed to currently-registered
-filters (including decompression filters) and/or format readers.
-The argument is a comma-separated list of individual options.
-Individual options have one of the following forms:
-.Bl -tag -compact -width indent
-.It Ar option=value
-The option/value pair will be provided to every module.
-Modules that do not accept an option with this name will ignore it.
-.It Ar option
-The option will be provided to every module with a value of
-.Dq 1 .
-.It Ar !option
-The option will be provided to every module with a NULL value.
-.It Ar module:option=value , Ar module:option , Ar module:!option
-As above, but the corresponding option and value will be provided
-only to modules whose name matches
-.Ar module .
-.El
-The return value will be
-.Cm ARCHIVE_OK
-if any module accepts the option, or
-.Cm ARCHIVE_WARN
-if no module accepted the option, or
-.Cm ARCHIVE_FATAL
-if there was a fatal error while attempting to process the option.
+to enable auto-detect for all formats and compression types
+currently supported by the library.
+.\"
+.Ss Set options
+See
+.Xr archive_read_set_options 3 .
+.\"
+.Ss Open archive
+See
+.Xr archive_read_open 3 .
 .Pp
-The currently supported options are:
-.Bl -tag -compact -width indent
-.It Format iso9660
-.Bl -tag -compact -width indent
-.It Cm joliet
-Support Joliet extensions.
-Defaults to enabled, use
-.Cm !joliet
-to disable.
-.El
-.El
-.It Fn archive_read_open
-The same as
-.Fn archive_read_open2 ,
-except that the skip callback is assumed to be
-.Dv NULL .
-.It Fn archive_read_open2
-Freeze the settings, open the archive, and prepare for reading entries.
-This is the most generic version of this call, which accepts
-four callback functions.
-Most clients will want to use
-.Fn archive_read_open_filename ,
-.Fn archive_read_open_FILE ,
-.Fn archive_read_open_fd ,
-or
-.Fn archive_read_open_memory
-instead.
-The library invokes the client-provided functions to obtain
-raw bytes from the archive.
-.It Fn archive_read_open_FILE
-Like
-.Fn archive_read_open ,
-except that it accepts a
+Once you have prepared the
+.Tn struct archive
+object, you call
+.Fn archive_read_open
+to actually open the archive and prepare it for reading.
+There are several variants of this function;
+the most basic expects you to provide pointers to several
+functions that can provide blocks of bytes from the archive.
+There are convenience forms that allow you to
+specify a filename, file descriptor,
 .Ft "FILE *"
-pointer.
-This function should not be used with tape drives or other devices
-that require strict I/O blocking.
-.It Fn archive_read_open_fd
-Like
-.Fn archive_read_open ,
-except that it accepts a file descriptor and block size rather than
-a set of function pointers.
-Note that the file descriptor will not be automatically closed at
-end-of-archive.
-This function is safe for use with tape drives or other blocked devices.
-.It Fn archive_read_open_file
-This is a deprecated synonym for
-.Fn archive_read_open_filename .
-.It Fn archive_read_open_filename
-Like
-.Fn archive_read_open ,
-except that it accepts a simple filename and a block size.
-A NULL filename represents standard input.
-This function is safe for use with tape drives or other blocked devices.
-.It Fn archive_read_open_memory
-Like
-.Fn archive_read_open ,
-except that it accepts a pointer and size of a block of
-memory containing the archive data.
-.It Fn archive_read_next_header
-Read the header for the next entry and return a pointer to
-a
-.Tn struct archive_entry .
-This is a convenience wrapper around
-.Fn archive_read_next_header2
-that reuses an internal
+object, or a block of memory from which to read the archive data.
+Note that the core library makes no assumptions about the
+size of the blocks read;
+callback functions are free to read whatever block size is
+most appropriate for the medium.
+.\"
+.Ss Consume archive
+See
+.Xr archive_read_header 3 ,
+.Xr archive_read_data 3
+and
+.Xr archive_read_extract 3 .
+.Pp
+Each archive entry consists of a header followed by a certain
+amount of data.
+You can obtain the next header with
+.Fn archive_read_next_header ,
+which returns a pointer to an
 .Tn struct archive_entry
-object for each request.
-.It Fn archive_read_next_header2
-Read the header for the next entry and populate the provided
-.Tn struct archive_entry .
-.It Fn archive_read_data
-Read data associated with the header just read.
-Internally, this is a convenience function that calls
+structure with information about the current archive element.
+If the entry is a regular file, then the header will be followed
+by the file data.
+You can use
+.Fn archive_read_data
+(which works much like the
+.Xr read 2
+system call)
+to read this data from the archive, or
 .Fn archive_read_data_block
-and fills any gaps with nulls so that callers see a single
-continuous stream of data.
-.It Fn archive_read_data_block
-Return the next available block of data for this entry.
-Unlike
-.Fn archive_read_data ,
-the
-.Fn archive_read_data_block
-function avoids copying data and allows you to correctly handle
-sparse files, as supported by some archive formats.
-The library guarantees that offsets will increase and that blocks
-will not overlap.
-Note that the blocks returned from this function can be much larger
-than the block size read from disk, due to compression
-and internal buffer optimizations.
-.It Fn archive_read_data_skip
-A convenience function that repeatedly calls
-.Fn archive_read_data_block
-to skip all of the data for this archive entry.
-.\" #if ARCHIVE_API_VERSION < 3
-.It Fn archive_read_data_into_buffer
-This function is deprecated and will be removed.
-Use
-.Fn archive_read_data
-instead.
-.\" #endif
-.It Fn archive_read_data_into_fd
-A convenience function that repeatedly calls
-.Fn archive_read_data_block
-to copy the entire entry to the provided file descriptor.
-.It Fn archive_read_extract , Fn archive_read_extract_set_skip_file
-A convenience function that wraps the corresponding
-.Xr archive_write_disk 3
-interfaces.
-The first call to
+which provides a slightly more efficient interface.
+You may prefer to use the higher-level
+.Fn archive_read_data_skip ,
+which reads and discards the data for this entry,
+.Fn archive_read_data_to_file ,
+which copies the data to the provided file descriptor, or
+.Fn archive_read_extract ,
+which recreates the specified entry on disk and copies data
+from the archive.
+In particular, note that
 .Fn archive_read_extract
-creates a restore object using
-.Xr archive_write_disk_new 3
-and
-.Xr archive_write_disk_set_standard_lookup 3 ,
-then transparently invokes
-.Xr archive_write_disk_set_options 3 ,
-.Xr archive_write_header 3 ,
-.Xr archive_write_data 3 ,
-and
-.Xr archive_write_finish_entry 3
-to create the entry on disk and copy data into it.
-The
-.Va flags
-argument is passed unmodified to
-.Xr archive_write_disk_set_options 3 .
-.It Fn archive_read_extract2
-This is another version of
-.Fn archive_read_extract
-that allows you to provide your own restore object.
-In particular, this allows you to override the standard lookup functions
-using
-.Xr archive_write_disk_set_group_lookup 3 ,
-and
-.Xr archive_write_disk_set_user_lookup 3 .
-Note that
-.Fn archive_read_extract2
-does not accept a
-.Va flags
-argument; you should use
-.Fn archive_write_disk_set_options
-to set the restore options yourself.
-.It Fn archive_read_extract_set_progress_callback
-Sets a pointer to a user-defined callback that can be used
-for updating progress displays during extraction.
-The progress function will be invoked during the extraction of large
-regular files.
-The progress function will be invoked with the pointer provided to this call.
-Generally, the data pointed to should include a reference to the archive
-object and the archive_entry object so that various statistics
-can be retrieved for the progress display.
-.It Fn archive_read_close
-Complete the archive and invoke the close callback.
-.It Fn archive_read_free
-Invokes
+uses the
+.Tn struct archive_entry
+structure that you provide it, which may differ from the
+entry just read from the archive.
+In particular, many applications will want to override the
+pathname, file permissions, or ownership.
+.\"
+.Ss Release resources
+See
+.Xr archive_read_free 3 .
+.Pp
+Once you have finished reading data from the archive, you
+should call
 .Fn archive_read_close
-if it was not invoked manually, then release all resources.
-Note: In libarchive 1.x, this function was declared to return
-.Ft void ,
-which made it impossible to detect certain errors when
-.Fn archive_read_close
-was invoked implicitly from this function.
-The declaration is corrected beginning with libarchive 2.0.
-.El
-.Pp
-Note that the library determines most of the relevant information about
-the archive by inspection.
-In particular, it automatically detects
-.Xr gzip 1
-or
-.Xr bzip2 1
-compression and transparently performs the appropriate decompression.
-It also automatically detects the archive format.
-.Pp
-A complete description of the
-.Tn struct archive
-and
-.Tn struct archive_entry
-objects can be found in the overview manual page for
-.Xr libarchive 3 .
-.Sh CLIENT CALLBACKS
-The callback functions must match the following prototypes:
-.Bl -item -offset indent
-.It
-.Ft typedef ssize_t
-.Fo archive_read_callback
-.Fa "struct archive *"
-.Fa "void *client_data"
-.Fa "const void **buffer"
-.Fc
-.It
-.\" #if ARCHIVE_API_VERSION < 2
-.Ft typedef int
-.Fo archive_skip_callback
-.Fa "struct archive *"
-.Fa "void *client_data"
-.Fa "size_t request"
-.Fc
-.\" #else
-.\" .Ft typedef off_t
-.\" .Fo archive_skip_callback
-.\" .Fa "struct archive *"
-.\" .Fa "void *client_data"
-.\" .Fa "off_t request"
-.\" .Fc
-.\" #endif
-.It
-.Ft typedef int
-.Fn archive_open_callback "struct archive *" "void *client_data"
-.It
-.Ft typedef int
-.Fn archive_close_callback "struct archive *" "void *client_data"
-.El
-.Pp
-The open callback is invoked by
-.Fn archive_open .
-It should return
-.Cm ARCHIVE_OK
-if the underlying file or data source is successfully
-opened.
-If the open fails, it should call
-.Fn archive_set_error
-to register an error code and message and return
-.Cm ARCHIVE_FATAL .
-.Pp
-The read callback is invoked whenever the library
-requires raw bytes from the archive.
-The read callback should read data into a buffer,
-set the
-.Li const void **buffer
-argument to point to the available data, and
-return a count of the number of bytes available.
-The library will invoke the read callback again
-only after it has consumed this data.
-The library imposes no constraints on the size
-of the data blocks returned.
-On end-of-file, the read callback should
-return zero.
-On error, the read callback should invoke
-.Fn archive_set_error
-to register an error code and message and
-return -1.
-.Pp
-The skip callback is invoked when the
-library wants to ignore a block of data.
-The return value is the number of bytes actually
-skipped, which may differ from the request.
-If the callback cannot skip data, it should return
-zero.
-If the skip callback is not provided (the
-function pointer is
-.Dv NULL ),
-the library will invoke the read function
-instead and simply discard the result.
-A skip callback can provide significant
-performance gains when reading uncompressed
-archives from slow disk drives or other media
-that can skip quickly.
-.Pp
-The close callback is invoked by archive_close when
-the archive processing is complete.
-The callback should return
-.Cm ARCHIVE_OK
-on success.
-On failure, the callback should invoke
-.Fn archive_set_error
-to register an error code and message and
-return
-.Cm ARCHIVE_FATAL.
+to close the archive, then call
+.Fn archive_read_free
+to release all resources, including all memory allocated by the library.
+.\"
 .Sh EXAMPLE
 The following illustrates basic usage of the library.
 In this example,
@@ -593,7 +173,7 @@
   mydata = malloc(sizeof(struct mydata));
   a = archive_read_new();
   mydata->name = name;
-  archive_read_support_compression_all(a);
+  archive_read_support_filter_all(a);
   archive_read_support_format_all(a);
   archive_read_open(a, mydata, myopen, myread, myclose);
   while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
@@ -632,62 +212,18 @@
   return (ARCHIVE_OK);
 }
 .Ed
-.Sh RETURN VALUES
-Most functions return zero on success, non-zero on error.
-The possible return codes include:
-.Cm ARCHIVE_OK
-(the operation succeeded),
-.Cm ARCHIVE_WARN
-(the operation succeeded but a non-critical error was encountered),
-.Cm ARCHIVE_EOF
-(end-of-archive was encountered),
-.Cm ARCHIVE_RETRY
-(the operation failed but can be retried),
-and
-.Cm ARCHIVE_FATAL
-(there was a fatal error; the archive should be closed immediately).
-Detailed error codes and textual descriptions are available from the
-.Fn archive_errno
-and
-.Fn archive_error_string
-functions.
-.Pp
-.Fn archive_read_new
-returns a pointer to a freshly allocated
-.Tn struct archive
-object.
-It returns
-.Dv NULL
-on error.
-.Pp
-.Fn archive_read_data
-returns a count of bytes actually read or zero at the end of the entry.
-On error, a value of
-.Cm ARCHIVE_FATAL ,
-.Cm ARCHIVE_WARN ,
-or
-.Cm ARCHIVE_RETRY
-is returned and an error code and textual description can be retrieved from the
-.Fn archive_errno
-and
-.Fn archive_error_string
-functions.
-.Pp
-The library expects the client callbacks to behave similarly.
-If there is an error, you can use
-.Fn archive_set_error
-to set an appropriate error code and description,
-then return one of the non-zero values above.
-(Note that the value eventually returned to the client may
-not be the same; many errors that are not critical at the level
-of basic I/O can prevent the archive from being properly read,
-thus most I/O errors eventually cause
-.Cm ARCHIVE_FATAL
-to be returned.)
 .\" .Sh ERRORS
 .Sh SEE ALSO
 .Xr tar 1 ,
-.Xr archive 3 ,
+.Xr libarchive 3 ,
+.Xr archive_read_new 3 ,
+.Xr archive_read_data 3 ,
+.Xr archive_read_extract 3 ,
+.Xr archive_read_filter 3 ,
+.Xr archive_read_format 3 ,
+.Xr archive_read_header 3 ,
+.Xr archive_read_open 3 ,
+.Xr archive_read_set_options 3 ,
 .Xr archive_util 3 ,
 .Xr tar 5
 .Sh HISTORY
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read.c
--- a/head/contrib/libarchive/libarchive/archive_read.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2003-2011 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -32,7 +32,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read.c 228773 2011-12-21 15:18:52Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -55,12 +55,24 @@
 
 #define minimum(a, b) (a < b ? a : b)
 
-static int	build_stream(struct archive_read *);
+static int	choose_filters(struct archive_read *);
 static int	choose_format(struct archive_read *);
-static int	cleanup_filters(struct archive_read *);
+static void	free_filters(struct archive_read *);
+static int	close_filters(struct archive_read *);
 static struct archive_vtable *archive_read_vtable(void);
+static int64_t	_archive_filter_bytes(struct archive *, int);
+static int	_archive_filter_code(struct archive *, int);
+static const char *_archive_filter_name(struct archive *, int);
+static int  _archive_filter_count(struct archive *);
 static int	_archive_read_close(struct archive *);
+static int	_archive_read_data_block(struct archive *,
+		    const void **, size_t *, int64_t *);
 static int	_archive_read_free(struct archive *);
+static int	_archive_read_next_header(struct archive *,
+		    struct archive_entry **);
+static int	_archive_read_next_header2(struct archive *,
+		    struct archive_entry *);
+static int64_t  advance_file_pointer(struct archive_read_filter *, int64_t);
 
 static struct archive_vtable *
 archive_read_vtable(void)
@@ -69,8 +81,16 @@
 	static int inited = 0;
 
 	if (!inited) {
+		av.archive_filter_bytes = _archive_filter_bytes;
+		av.archive_filter_code = _archive_filter_code;
+		av.archive_filter_name = _archive_filter_name;
+		av.archive_filter_count = _archive_filter_count;
+		av.archive_read_data_block = _archive_read_data_block;
+		av.archive_read_next_header = _archive_read_next_header;
+		av.archive_read_next_header2 = _archive_read_next_header2;
 		av.archive_free = _archive_read_free;
 		av.archive_close = _archive_read_close;
+		inited = 1;
 	}
 	return (&av);
 }
@@ -90,7 +110,7 @@
 	a->archive.magic = ARCHIVE_READ_MAGIC;
 
 	a->archive.state = ARCHIVE_STATE_NEW;
-	a->entry = archive_entry_new();
+	a->entry = archive_entry_new2(&a->archive);
 	a->archive.vtable = archive_read_vtable();
 
 	return (&a->archive);
@@ -100,132 +120,19 @@
  * Record the do-not-extract-to file. This belongs in archive_read_extract.c.
  */
 void
-archive_read_extract_set_skip_file(struct archive *_a, dev_t d, ino_t i)
+archive_read_extract_set_skip_file(struct archive *_a, int64_t d, int64_t i)
 {
 	struct archive_read *a = (struct archive_read *)_a;
-	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY,
-	    "archive_read_extract_set_skip_file");
+
+	if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+		ARCHIVE_STATE_ANY, "archive_read_extract_set_skip_file"))
+		return;
+	a->skip_file_set = 1;
 	a->skip_file_dev = d;
 	a->skip_file_ino = i;
 }
 
 /*
- * Set read options for the format.
- */
-int
-archive_read_set_format_options(struct archive *_a, const char *s)
-{
-	struct archive_read *a;
-	struct archive_format_descriptor *format;
-	char key[64], val[64];
-	char *valp;
-	size_t i;
-	int len, r;
-
-	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
-	    "archive_read_set_format_options");
-
-	if (s == NULL || *s == '\0')
-		return (ARCHIVE_OK);
-	a = (struct archive_read *)_a;
-	__archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,
-	    ARCHIVE_STATE_NEW, "archive_read_set_format_options");
-	len = 0;
-	for (i = 0; i < sizeof(a->formats)/sizeof(a->formats[0]); i++) {
-		format = &a->formats[i];
-		if (format == NULL || format->options == NULL ||
-		    format->name == NULL)
-			/* This format does not support option. */
-			continue;
-
-		while ((len = __archive_parse_options(s, format->name,
-		    sizeof(key), key, sizeof(val), val)) > 0) {
-			valp = val[0] == '\0' ? NULL : val;
-			a->format = format;
-			r = format->options(a, key, valp);
-			a->format = NULL;
-			if (r == ARCHIVE_FATAL)
-				return (r);
-			s += len;
-		}
-	}
-	if (len < 0) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Illegal format options.");
-		return (ARCHIVE_WARN);
-	}
-	return (ARCHIVE_OK);
-}
-
-/*
- * Set read options for the filter.
- */
-int
-archive_read_set_filter_options(struct archive *_a, const char *s)
-{
-	struct archive_read *a;
-	struct archive_read_filter *filter;
-	struct archive_read_filter_bidder *bidder;
-	char key[64], val[64];
-	int len, r;
-
-	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
-	    "archive_read_set_filter_options");
-
-	if (s == NULL || *s == '\0')
-		return (ARCHIVE_OK);
-	a = (struct archive_read *)_a;
-	__archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,
-	    ARCHIVE_STATE_NEW, "archive_read_set_filter_options");
-	len = 0;
-	for (filter = a->filter; filter != NULL; filter = filter->upstream) {
-		bidder = filter->bidder;
-		if (bidder == NULL)
-			continue;
-		if (bidder->options == NULL)
-			/* This bidder does not support option */
-			continue;
-		while ((len = __archive_parse_options(s, filter->name,
-		    sizeof(key), key, sizeof(val), val)) > 0) {
-			if (val[0] == '\0')
-				r = bidder->options(bidder, key, NULL);
-			else
-				r = bidder->options(bidder, key, val);
-			if (r == ARCHIVE_FATAL)
-				return (r);
-			s += len;
-		}
-	}
-	if (len < 0) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Illegal format options.");
-		return (ARCHIVE_WARN);
-	}
-	return (ARCHIVE_OK);
-}
-
-/*
- * Set read options for the format and the filter.
- */
-int
-archive_read_set_options(struct archive *_a, const char *s)
-{
-	int r;
-
-	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
-	    "archive_read_set_options");
-	archive_clear_error(_a);
-
-	r = archive_read_set_format_options(_a, s);
-	if (r != ARCHIVE_OK)
-		return (r);
-	r = archive_read_set_filter_options(_a, s);
-	if (r != ARCHIVE_OK)
-		return (r);
-	return (ARCHIVE_OK);
-}
-
-/*
  * Open the archive
  */
 int
@@ -234,9 +141,30 @@
     archive_close_callback *client_closer)
 {
 	/* Old archive_read_open() is just a thin shell around
-	 * archive_read_open2. */
-	return archive_read_open2(a, client_data, client_opener,
-	    client_reader, NULL, client_closer);
+	 * archive_read_open1. */
+	archive_read_set_open_callback(a, client_opener);
+	archive_read_set_read_callback(a, client_reader);
+	archive_read_set_close_callback(a, client_closer);
+	archive_read_set_callback_data(a, client_data);
+	return archive_read_open1(a);
+}
+
+
+int
+archive_read_open2(struct archive *a, void *client_data,
+    archive_open_callback *client_opener,
+    archive_read_callback *client_reader,
+    archive_skip_callback *client_skipper,
+    archive_close_callback *client_closer)
+{
+	/* Old archive_read_open2() is just a thin shell around
+	 * archive_read_open1. */
+	archive_read_set_callback_data(a, client_data);
+	archive_read_set_open_callback(a, client_opener);
+	archive_read_set_read_callback(a, client_reader);
+	archive_read_set_skip_callback(a, client_skipper);
+	archive_read_set_close_callback(a, client_closer);
+	return archive_read_open1(a);
 }
 
 static ssize_t
@@ -245,33 +173,70 @@
 	ssize_t r;
 	r = (self->archive->client.reader)(&self->archive->archive,
 	    self->data, buff);
-	self->archive->archive.raw_position += r;
 	return (r);
 }
 
 static int64_t
 client_skip_proxy(struct archive_read_filter *self, int64_t request)
 {
-	int64_t ask, get, total;
-	/* Limit our maximum seek request to 1GB on platforms
-	* with 32-bit off_t (such as Windows). */
-	int64_t skip_limit = ((int64_t)1) << (sizeof(off_t) * 8 - 2);
+	if (request < 0)
+		__archive_errx(1, "Negative skip requested.");
+	if (request == 0)
+		return 0;
 
-	if (self->archive->client.skipper == NULL)
-		return (0);
-	total = 0;
-	for (;;) {
-		ask = request;
-		if (ask > skip_limit)
-			ask = skip_limit;
-		get = (self->archive->client.skipper)(&self->archive->archive,
-			self->data, ask);
-		if (get == 0)
-			return (total);
-		request -= get;
-		self->archive->archive.raw_position += get;
-		total += get;
+	if (self->archive->client.skipper != NULL) {
+		/* Seek requests over 1GiB are broken down into
+		 * multiple seeks.  This avoids overflows when the
+		 * requests get passed through 32-bit arguments. */
+		int64_t skip_limit = (int64_t)1 << 30;
+		int64_t total = 0;
+		for (;;) {
+			int64_t get, ask = request;
+			if (ask > skip_limit)
+				ask = skip_limit;
+			get = (self->archive->client.skipper)(&self->archive->archive,
+			    self->data, ask);
+			if (get == 0)
+				return (total);
+			request -= get;
+			total += get;
+		}
+		return total;
+	} else if (self->archive->client.seeker != NULL
+		&& request > 64 * 1024) {
+		/* If the client provided a seeker but not a skipper,
+		 * we can use the seeker to skip forward.
+		 *
+		 * Note: This isn't always a good idea.  The client
+		 * skipper is allowed to skip by less than requested
+		 * if it needs to maintain block alignment.  The
+		 * seeker is not allowed to play such games, so using
+		 * the seeker here may be a performance loss compared
+		 * to just reading and discarding.  That's why we
+		 * only do this for skips of over 64k.
+		 */
+		int64_t before = self->position;
+		int64_t after = (self->archive->client.seeker)(&self->archive->archive,
+		    self->data, request, SEEK_CUR);
+		if (after != before + request)
+			return ARCHIVE_FATAL;
+		return after - before;
 	}
+	return 0;
+}
+
+static int64_t
+client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence)
+{
+	/* DO NOT use the skipper here!  If we transparently handled
+	 * forward seek here by using the skipper, that will break
+	 * other libarchive code that assumes a successful forward
+	 * seek means it can also seek backwards.
+	 */
+	if (self->archive->client.seeker == NULL)
+		return (ARCHIVE_FAILED);
+	return (self->archive->client.seeker)(&self->archive->archive,
+	    self->data, offset, whence);
 }
 
 static int
@@ -282,65 +247,134 @@
 	if (self->archive->client.closer != NULL)
 		r = (self->archive->client.closer)((struct archive *)self->archive,
 		    self->data);
-	self->data = NULL;
 	return (r);
 }
 
+int
+archive_read_set_open_callback(struct archive *_a,
+    archive_open_callback *client_opener)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_open_callback");
+	a->client.opener = client_opener;
+	return ARCHIVE_OK;
+}
 
 int
-archive_read_open2(struct archive *_a, void *client_data,
-    archive_open_callback *client_opener,
-    archive_read_callback *client_reader,
-    archive_skip_callback *client_skipper,
+archive_read_set_read_callback(struct archive *_a,
+    archive_read_callback *client_reader)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_read_callback");
+	a->client.reader = client_reader;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_set_skip_callback(struct archive *_a,
+    archive_skip_callback *client_skipper)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_skip_callback");
+	a->client.skipper = client_skipper;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_set_seek_callback(struct archive *_a,
+    archive_seek_callback *client_seeker)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_seek_callback");
+	a->client.seeker = client_seeker;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_set_close_callback(struct archive *_a,
     archive_close_callback *client_closer)
 {
 	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_close_callback");
+	a->client.closer = client_closer;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_set_callback_data(struct archive *_a, void *client_data)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_callback_data");
+	a->client.data = client_data;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_open1(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
 	struct archive_read_filter *filter;
-	int e;
+	int slot, e;
 
-	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
 	    "archive_read_open");
 	archive_clear_error(&a->archive);
 
-	if (client_reader == NULL)
-		__archive_errx(1,
+	if (a->client.reader == NULL) {
+		archive_set_error(&a->archive, EINVAL,
 		    "No reader function provided to archive_read_open");
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
 
 	/* Open data source. */
-	if (client_opener != NULL) {
-		e =(client_opener)(&a->archive, client_data);
+	if (a->client.opener != NULL) {
+		e =(a->client.opener)(&a->archive, a->client.data);
 		if (e != 0) {
 			/* If the open failed, call the closer to clean up. */
-			if (client_closer)
-				(client_closer)(&a->archive, client_data);
+			if (a->client.closer)
+				(a->client.closer)(&a->archive, a->client.data);
 			return (e);
 		}
 	}
 
-	/* Save the client functions and mock up the initial source. */
-	a->client.reader = client_reader;
-	a->client.skipper = client_skipper;
-	a->client.closer = client_closer;
-
 	filter = calloc(1, sizeof(*filter));
 	if (filter == NULL)
 		return (ARCHIVE_FATAL);
 	filter->bidder = NULL;
 	filter->upstream = NULL;
 	filter->archive = a;
-	filter->data = client_data;
+	filter->data = a->client.data;
 	filter->read = client_read_proxy;
 	filter->skip = client_skip_proxy;
+	filter->seek = client_seek_proxy;
 	filter->close = client_close_proxy;
 	filter->name = "none";
 	filter->code = ARCHIVE_COMPRESSION_NONE;
 	a->filter = filter;
 
 	/* Build out the input pipeline. */
-	e = build_stream(a);
-	if (e == ARCHIVE_OK)
-		a->archive.state = ARCHIVE_STATE_HEADER;
+	e = choose_filters(a);
+	if (e < ARCHIVE_WARN) {
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
 
+	slot = choose_format(a);
+	if (slot < 0) {
+		close_filters(a);
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+	a->format = &(a->formats[slot]);
+
+	a->archive.state = ARCHIVE_STATE_HEADER;
 	return (e);
 }
 
@@ -350,7 +384,7 @@
  * building the pipeline.
  */
 static int
-build_stream(struct archive_read *a)
+choose_filters(struct archive_read *a)
 {
 	int number_bidders, i, bid, best_bid;
 	struct archive_read_filter_bidder *bidder, *best_bidder;
@@ -377,10 +411,11 @@
 
 		/* If no bidder, we're done. */
 		if (best_bidder == NULL) {
-			/* Verify the final pipelin by asking it for some data. */
+			/* Verify the filter by asking it for some data. */
 			__archive_read_filter_ahead(a->filter, 1, &avail);
 			if (avail < 0) {
-				cleanup_filters(a);
+				close_filters(a);
+				free_filters(a);
 				return (ARCHIVE_FATAL);
 			}
 			a->archive.compression_name = a->filter->name;
@@ -398,8 +433,9 @@
 		a->filter = filter;
 		r = (best_bidder->init)(a->filter);
 		if (r != ARCHIVE_OK) {
-			cleanup_filters(a);
-			return (r);
+			close_filters(a);
+			free_filters(a);
+			return (ARCHIVE_FATAL);
 		}
 	}
 }
@@ -407,60 +443,49 @@
 /*
  * Read header of next entry.
  */
-int
-archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
+static int
+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
 {
 	struct archive_read *a = (struct archive_read *)_a;
-	int slot, ret;
+	int r1 = ARCHIVE_OK, r2;
 
-	__archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
 	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
 	    "archive_read_next_header");
 
-	++_a->file_count;
 	archive_entry_clear(entry);
 	archive_clear_error(&a->archive);
 
 	/*
-	 * If no format has yet been chosen, choose one.
-	 */
-	if (a->format == NULL) {
-		slot = choose_format(a);
-		if (slot < 0) {
-			a->archive.state = ARCHIVE_STATE_FATAL;
-			return (ARCHIVE_FATAL);
-		}
-		a->format = &(a->formats[slot]);
-	}
-
-	/*
 	 * If client didn't consume entire data, skip any remainder
 	 * (This is especially important for GNU incremental directories.)
 	 */
 	if (a->archive.state == ARCHIVE_STATE_DATA) {
-		ret = archive_read_data_skip(&a->archive);
-		if (ret == ARCHIVE_EOF) {
-			archive_set_error(&a->archive, EIO, "Premature end-of-file.");
+		r1 = archive_read_data_skip(&a->archive);
+		if (r1 == ARCHIVE_EOF)
+			archive_set_error(&a->archive, EIO,
+			    "Premature end-of-file.");
+		if (r1 == ARCHIVE_EOF || r1 == ARCHIVE_FATAL) {
 			a->archive.state = ARCHIVE_STATE_FATAL;
 			return (ARCHIVE_FATAL);
 		}
-		if (ret != ARCHIVE_OK)
-			return (ret);
 	}
 
-	/* Record start-of-header. */
-	a->header_position = a->archive.file_position;
+	/* Record start-of-header offset in uncompressed stream. */
+	a->header_position = a->filter->position;
 
-	ret = (a->format->read_header)(a, entry);
+	++_a->file_count;
+	r2 = (a->format->read_header)(a, entry);
 
 	/*
 	 * EOF and FATAL are persistent at this layer.  By
 	 * modifying the state, we guarantee that future calls to
 	 * read a header or read data will fail.
 	 */
-	switch (ret) {
+	switch (r2) {
 	case ARCHIVE_EOF:
 		a->archive.state = ARCHIVE_STATE_EOF;
+		--_a->file_count;/* Revert a file counter. */
 		break;
 	case ARCHIVE_OK:
 		a->archive.state = ARCHIVE_STATE_DATA;
@@ -477,16 +502,17 @@
 
 	a->read_data_output_offset = 0;
 	a->read_data_remaining = 0;
-	return (ret);
+	/* EOF always wins; otherwise return the worst error. */
+	return (r2 < r1 || r2 == ARCHIVE_EOF) ? r2 : r1;
 }
 
 int
-archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
 {
 	int ret;
 	struct archive_read *a = (struct archive_read *)_a;
 	*entryp = NULL;
-	ret = archive_read_next_header2(_a, a->entry);
+	ret = _archive_read_next_header2(_a, a->entry);
 	*entryp = a->entry;
 	return ret;
 }
@@ -507,13 +533,15 @@
 	best_bid = -1;
 	best_bid_slot = -1;
 
-	/* Set up a->format and a->pformat_data for convenience of bidders. */
+	/* Set up a->format for convenience of bidders. */
 	a->format = &(a->formats[0]);
 	for (i = 0; i < slots; i++, a->format++) {
 		if (a->format->bid) {
-			bid = (a->format->bid)(a);
+			bid = (a->format->bid)(a, best_bid);
 			if (bid == ARCHIVE_FATAL)
 				return (ARCHIVE_FATAL);
+			if (a->filter->position != 0)
+				__archive_read_seek(a, 0, SEEK_SET);
 			if ((bid > best_bid) || (best_bid_slot < 0)) {
 				best_bid = bid;
 				best_bid_slot = i;
@@ -525,10 +553,11 @@
 	 * There were no bidders; this is a serious programmer error
 	 * and demands a quick and definitive abort.
 	 */
-	if (best_bid_slot < 0)
-		__archive_errx(1, "No formats were registered; you must "
-		    "invoke at least one archive_read_support_format_XXX "
-		    "function in order to successfully read an archive.");
+	if (best_bid_slot < 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "No formats registered");
+		return (ARCHIVE_FATAL);
+	}
 
 	/*
 	 * There were bidders, but no non-zero bids; this means we
@@ -551,7 +580,7 @@
 archive_read_header_position(struct archive *_a)
 {
 	struct archive_read *a = (struct archive_read *)_a;
-	__archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
 	    ARCHIVE_STATE_ANY, "archive_read_header_position");
 	return (a->header_position);
 }
@@ -583,7 +612,7 @@
 	while (s > 0) {
 		if (a->read_data_remaining == 0) {
 			read_buf = a->read_data_block;
-			r = archive_read_data_block(&a->archive, &read_buf,
+			r = _archive_read_data_block(&a->archive, &read_buf,
 			    &a->read_data_remaining, &a->read_data_offset);
 			a->read_data_block = read_buf;
 			if (r == ARCHIVE_EOF)
@@ -604,7 +633,7 @@
 		}
 
 		/* Compute the amount of zero padding needed. */
-		if (a->read_data_output_offset + (off_t)s <
+		if (a->read_data_output_offset + (int64_t)s <
 		    a->read_data_offset) {
 			len = s;
 		} else if (a->read_data_output_offset <
@@ -639,22 +668,6 @@
 	return (bytes_read);
 }
 
-#if ARCHIVE_API_VERSION < 3
-/*
- * Obsolete function provided for compatibility only.  Note that the API
- * of this function doesn't allow the caller to detect if the remaining
- * data from the archive entry is shorter than the buffer provided, or
- * even if an error occurred while reading data.
- */
-int
-archive_read_data_into_buffer(struct archive *a, void *d, ssize_t len)
-{
-
-	archive_read_data(a, d, len);
-	return (ARCHIVE_OK);
-}
-#endif
-
 /*
  * Skip over all remaining data in this entry.
  */
@@ -665,9 +678,9 @@
 	int r;
 	const void *buff;
 	size_t size;
-	off_t offset;
+	int64_t offset;
 
-	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
 	    "archive_read_data_skip");
 
 	if (a->format->read_data_skip != NULL)
@@ -694,12 +707,12 @@
  * Returns ARCHIVE_OK if the operation is successful, ARCHIVE_EOF if
  * the end of entry is encountered.
  */
-int
-archive_read_data_block(struct archive *_a,
-    const void **buff, size_t *size, off_t *offset)
+static int
+_archive_read_data_block(struct archive *_a,
+    const void **buff, size_t *size, int64_t *offset)
 {
 	struct archive_read *a = (struct archive_read *)_a;
-	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
 	    "archive_read_data_block");
 
 	if (a->format->read_data == NULL) {
@@ -712,69 +725,79 @@
 	return (a->format->read_data)(a, buff, size, offset);
 }
 
+static int
+close_filters(struct archive_read *a)
+{
+	struct archive_read_filter *f = a->filter;
+	int r = ARCHIVE_OK;
+	/* Close each filter in the pipeline. */
+	while (f != NULL) {
+		struct archive_read_filter *t = f->upstream;
+		if (!f->closed && f->close != NULL) {
+			int r1 = (f->close)(f);
+			f->closed = 1;
+			if (r1 < r)
+				r = r1;
+		}
+		free(f->buffer);
+		f->buffer = NULL;
+		f = t;
+	}
+	return r;
+}
+
+static void
+free_filters(struct archive_read *a)
+{
+	while (a->filter != NULL) {
+		struct archive_read_filter *t = a->filter->upstream;
+		free(a->filter);
+		a->filter = t;
+	}
+}
+
 /*
- * Close the file and release most resources.
- *
- * Be careful: client might just call read_new and then read_finish.
- * Don't assume we actually read anything or performed any non-trivial
- * initialization.
+ * return the count of # of filters in use
+ */
+static int
+_archive_filter_count(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter *p = a->filter;
+	int count = 0;
+	while(p) {
+		count++;
+		p = p->upstream;
+	}
+	return count;
+}
+
+/*
+ * Close the file and all I/O.
  */
 static int
 _archive_read_close(struct archive *_a)
 {
 	struct archive_read *a = (struct archive_read *)_a;
 	int r = ARCHIVE_OK, r1 = ARCHIVE_OK;
-	size_t i, n;
 
-	__archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,
-	    ARCHIVE_STATE_ANY, "archive_read_close");
+	archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close");
+	if (a->archive.state == ARCHIVE_STATE_CLOSED)
+		return (ARCHIVE_OK);
 	archive_clear_error(&a->archive);
 	a->archive.state = ARCHIVE_STATE_CLOSED;
 
-
-	/* Call cleanup functions registered by optional components. */
-	if (a->cleanup_archive_extract != NULL)
-		r = (a->cleanup_archive_extract)(a);
-
 	/* TODO: Clean up the formatters. */
 
 	/* Release the filter objects. */
-	r1 = cleanup_filters(a);
+	r1 = close_filters(a);
 	if (r1 < r)
 		r = r1;
 
-	/* Release the bidder objects. */
-	n = sizeof(a->bidders)/sizeof(a->bidders[0]);
-	for (i = 0; i < n; i++) {
-		if (a->bidders[i].free != NULL) {
-			r1 = (a->bidders[i].free)(&a->bidders[i]);
-			if (r1 < r)
-				r = r1;
-		}
-	}
-
 	return (r);
 }
 
-static int
-cleanup_filters(struct archive_read *a)
-{
-	int r = ARCHIVE_OK;
-	/* Clean up the filter pipeline. */
-	while (a->filter != NULL) {
-		struct archive_read_filter *t = a->filter->upstream;
-		if (a->filter->close != NULL) {
-			int r1 = (a->filter->close)(a->filter);
-			if (r1 < r)
-				r = r1;
-		}
-		free(a->filter->buffer);
-		free(a->filter);
-		a->filter = t;
-	}
-	return r;
-}
-
 /*
  * Release memory and other resources.
  */
@@ -782,15 +805,22 @@
 _archive_read_free(struct archive *_a)
 {
 	struct archive_read *a = (struct archive_read *)_a;
-	int i;
+	int i, n;
 	int slots;
 	int r = ARCHIVE_OK;
 
-	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY,
-	    "archive_read_free");
-	if (a->archive.state != ARCHIVE_STATE_CLOSED)
+	if (_a == NULL)
+		return (ARCHIVE_OK);
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");
+	if (a->archive.state != ARCHIVE_STATE_CLOSED
+	    && a->archive.state != ARCHIVE_STATE_FATAL)
 		r = archive_read_close(&a->archive);
 
+	/* Call cleanup functions registered by optional components. */
+	if (a->cleanup_archive_extract != NULL)
+		r = (a->cleanup_archive_extract)(a);
+
 	/* Cleanup format-specific data. */
 	slots = sizeof(a->formats) / sizeof(a->formats[0]);
 	for (i = 0; i < slots; i++) {
@@ -799,14 +829,71 @@
 			(a->formats[i].cleanup)(a);
 	}
 
+	/* Free the filters */
+	free_filters(a);
+
+	/* Release the bidder objects. */
+	n = sizeof(a->bidders)/sizeof(a->bidders[0]);
+	for (i = 0; i < n; i++) {
+		if (a->bidders[i].free != NULL) {
+			int r1 = (a->bidders[i].free)(&a->bidders[i]);
+			if (r1 < r)
+				r = r1;
+		}
+	}
+
 	archive_string_free(&a->archive.error_string);
 	if (a->entry)
 		archive_entry_free(a->entry);
 	a->archive.magic = 0;
+	__archive_clean(&a->archive);
 	free(a);
-#if ARCHIVE_API_VERSION > 1
 	return (r);
-#endif
+}
+
+static struct archive_read_filter *
+get_filter(struct archive *_a, int n)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter *f = a->filter;
+	/* We use n == -1 for 'the last filter', which is always the client proxy. */
+	if (n == -1 && f != NULL) {
+		struct archive_read_filter *last = f;
+		f = f->upstream;
+		while (f != NULL) {
+			last = f;
+			f = f->upstream;
+		}
+		return (last);
+	}
+	if (n < 0)
+		return NULL;
+	while (n > 0 && f != NULL) {
+		f = f->upstream;
+		--n;
+	}
+	return (f);
+}
+
+static int
+_archive_filter_code(struct archive *_a, int n)
+{
+	struct archive_read_filter *f = get_filter(_a, n);
+	return f == NULL ? -1 : f->code;
+}
+
+static const char *
+_archive_filter_name(struct archive *_a, int n)
+{
+	struct archive_read_filter *f = get_filter(_a, n);
+	return f == NULL ? NULL : f->name;
+}
+
+static int64_t
+_archive_filter_bytes(struct archive *_a, int n)
+{
+	struct archive_read_filter *f = get_filter(_a, n);
+	return f == NULL ? -1 : f->position;
 }
 
 /*
@@ -817,16 +904,16 @@
 __archive_read_register_format(struct archive_read *a,
     void *format_data,
     const char *name,
-    int (*bid)(struct archive_read *),
+    int (*bid)(struct archive_read *, int),
     int (*options)(struct archive_read *, const char *, const char *),
     int (*read_header)(struct archive_read *, struct archive_entry *),
-    int (*read_data)(struct archive_read *, const void **, size_t *, off_t *),
+    int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *),
     int (*read_data_skip)(struct archive_read *),
     int (*cleanup)(struct archive_read *))
 {
 	int i, number_slots;
 
-	__archive_check_magic(&a->archive,
+	archive_check_magic(&a->archive,
 	    ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
 	    "__archive_read_register_format");
 
@@ -848,74 +935,68 @@
 		}
 	}
 
-	__archive_errx(1, "Not enough slots for format registration");
-	return (ARCHIVE_FATAL); /* Never actually called. */
+	archive_set_error(&a->archive, ENOMEM,
+	    "Not enough slots for format registration");
+	return (ARCHIVE_FATAL);
 }
 
 /*
  * Used internally by decompression routines to register their bid and
  * initialization functions.
  */
-struct archive_read_filter_bidder *
-__archive_read_get_bidder(struct archive_read *a)
+int
+__archive_read_get_bidder(struct archive_read *a,
+    struct archive_read_filter_bidder **bidder)
 {
 	int i, number_slots;
 
-	__archive_check_magic(&a->archive,
-	    ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
-	    "__archive_read_get_bidder");
-
 	number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]);
 
 	for (i = 0; i < number_slots; i++) {
 		if (a->bidders[i].bid == NULL) {
 			memset(a->bidders + i, 0, sizeof(a->bidders[0]));
-			return (a->bidders + i);
+			*bidder = (a->bidders + i);
+			return (ARCHIVE_OK);
 		}
 	}
 
-	__archive_errx(1, "Not enough slots for compression registration");
-	return (NULL); /* Never actually executed. */
+	archive_set_error(&a->archive, ENOMEM,
+	    "Not enough slots for filter registration");
+	return (ARCHIVE_FATAL);
 }
 
 /*
- * The next three functions comprise the peek/consume internal I/O
- * system used by archive format readers.  This system allows fairly
- * flexible read-ahead and allows the I/O code to operate in a
- * zero-copy manner most of the time.
+ * The next section implements the peek/consume internal I/O
+ * system used by archive readers.  This system allows simple
+ * read-ahead for consumers while preserving zero-copy operation
+ * most of the time.
+ *
+ * The two key operations:
+ *  * The read-ahead function returns a pointer to a block of data
+ *    that satisfies a minimum request.
+ *  * The consume function advances the file pointer.
  *
  * In the ideal case, filters generate blocks of data
  * and __archive_read_ahead() just returns pointers directly into
  * those blocks.  Then __archive_read_consume() just bumps those
  * pointers.  Only if your request would span blocks does the I/O
  * layer use a copy buffer to provide you with a contiguous block of
- * data.  The __archive_read_skip() is an optimization; it scans ahead
- * very quickly (it usually translates into a seek() operation if
- * you're reading uncompressed disk files).
+ * data.
  *
  * A couple of useful idioms:
  *  * "I just want some data."  Ask for 1 byte and pay attention to
  *    the "number of bytes available" from __archive_read_ahead().
- *    You can consume more than you asked for; you just can't consume
- *    more than is available.  If you consume everything that's
- *    immediately available, the next read_ahead() call will pull
- *    the next block.
+ *    Consume whatever you actually use.
  *  * "I want to output a large block of data."  As above, ask for 1 byte,
- *    emit all that's available (up to whatever limit you have), then
- *    repeat until you're done.
+ *    emit all that's available (up to whatever limit you have), consume
+ *    it all, then repeat until you're done.  This effectively means that
+ *    you're passing along the blocks that came from your provider.
  *  * "I want to peek ahead by a large amount."  Ask for 4k or so, then
  *    double and repeat until you get an error or have enough.  Note
  *    that the I/O layer will likely end up expanding its copy buffer
  *    to fit your request, so use this technique cautiously.  This
  *    technique is used, for example, by some of the format tasting
  *    code that has uncertain look-ahead needs.
- *
- * TODO: Someday, provide a more generic __archive_read_seek() for
- * those cases where it's useful.  This is tricky because there are lots
- * of cases where seek() is not available (reading gzip data from a
- * network socket, for instance), so there needs to be a good way to
- * communicate whether seek() is available and users of that interface
- * need to use non-seeking strategies whenever seek() is not available.
  */
 
 /*
@@ -924,8 +1005,8 @@
  *    in the current buffer, which may be much larger than requested.
  *  * If end-of-file, *avail gets set to zero.
  *  * If error, *avail gets error code.
- *  * If request can be met, returns pointer to data, returns NULL
- *    if request is not met.
+ *  * If request can be met, returns pointer to data.
+ *  * If minimum request cannot be met, returns NULL.
  *
  * Note: If you just want "some data", ask for 1 byte and pay attention
  * to *avail, which will have the actual amount available.  If you
@@ -935,17 +1016,6 @@
  * Important:  This does NOT move the file pointer.  See
  * __archive_read_consume() below.
  */
-
-/*
- * This is tricky.  We need to provide our clients with pointers to
- * contiguous blocks of memory but we want to avoid copying whenever
- * possible.
- *
- * Mostly, this code returns pointers directly into the block of data
- * provided by the client_read routine.  It can do this unless the
- * request would split across blocks.  In that case, we have to copy
- * into an internal buffer to combine reads.
- */
 const void *
 __archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
 {
@@ -1034,7 +1104,6 @@
 					*avail = filter->avail;
 				return (NULL);
 			}
-			filter->position += bytes_read;
 			filter->client_total = bytes_read;
 			filter->client_avail = filter->client_total;
 			filter->client_next = filter->client_buff;
@@ -1114,136 +1183,160 @@
 }
 
 /*
- * Move the file pointer forward.  This should be called after
- * __archive_read_ahead() returns data to you.  Don't try to move
- * ahead by more than the amount of data available according to
- * __archive_read_ahead().
+ * Move the file pointer forward.
  */
-/*
- * Mark the appropriate data as used.  Note that the request here will
- * often be much smaller than the size of the previous read_ahead
- * request.
- */
-ssize_t
-__archive_read_consume(struct archive_read *a, size_t request)
+int64_t
+__archive_read_consume(struct archive_read *a, int64_t request)
 {
-	ssize_t r;
-	r = __archive_read_filter_consume(a->filter, request);
-	a->archive.file_position += r;
-	return (r);
+	return (__archive_read_filter_consume(a->filter, request));
 }
 
-ssize_t
+int64_t
 __archive_read_filter_consume(struct archive_read_filter * filter,
-    size_t request)
+    int64_t request)
 {
-	if (filter->avail > 0) {
-		/* Read came from copy buffer. */
-		filter->next += request;
-		filter->avail -= request;
-	} else {
-		/* Read came from client buffer. */
-		filter->client_next += request;
-		filter->client_avail -= request;
-	}
-	return (request);
-}
+	int64_t skipped;
 
-/*
- * Move the file pointer ahead by an arbitrary amount.  If you're
- * reading uncompressed data from a disk file, this will actually
- * translate into a seek() operation.  Even in cases where seek()
- * isn't feasible, this at least pushes the read-and-discard loop
- * down closer to the data source.
- */
-int64_t
-__archive_read_skip(struct archive_read *a, int64_t request)
-{
-	int64_t skipped = __archive_read_skip_lenient(a, request);
+	if (request == 0)
+		return 0;
+
+	skipped = advance_file_pointer(filter, request);
 	if (skipped == request)
 		return (skipped);
 	/* We hit EOF before we satisfied the skip request. */
-	if (skipped < 0)  // Map error code to 0 for error message below.
+	if (skipped < 0)  /* Map error code to 0 for error message below. */
 		skipped = 0;
-	archive_set_error(&a->archive,
+	archive_set_error(&filter->archive->archive,
 	    ARCHIVE_ERRNO_MISC,
 	    "Truncated input file (needed %jd bytes, only %jd available)",
 	    (intmax_t)request, (intmax_t)skipped);
 	return (ARCHIVE_FATAL);
 }
 
-int64_t
-__archive_read_skip_lenient(struct archive_read *a, int64_t request)
-{
-	int64_t skipped = __archive_read_filter_skip(a->filter, request);
-	if (skipped > 0)
-		a->archive.file_position += skipped;
-	return (skipped);
-}
-
-int64_t
-__archive_read_filter_skip(struct archive_read_filter *filter, int64_t request)
+/*
+ * Advance the file pointer by the amount requested.
+ * Returns the amount actually advanced, which may be less than the
+ * request if EOF is encountered first.
+ * Returns a negative value if there's an I/O error.
+ */
+static int64_t
+advance_file_pointer(struct archive_read_filter *filter, int64_t request)
 {
 	int64_t bytes_skipped, total_bytes_skipped = 0;
+	ssize_t bytes_read;
 	size_t min;
 
 	if (filter->fatal)
 		return (-1);
-	/*
-	 * If there is data in the buffers already, use that first.
-	 */
+
+	/* Use up the copy buffer first. */
 	if (filter->avail > 0) {
-		min = minimum(request, (off_t)filter->avail);
-		bytes_skipped = __archive_read_filter_consume(filter, min);
-		request -= bytes_skipped;
-		total_bytes_skipped += bytes_skipped;
+		min = minimum(request, (int64_t)filter->avail);
+		filter->next += min;
+		filter->avail -= min;
+		request -= min;
+		filter->position += min;
+		total_bytes_skipped += min;
 	}
+
+	/* Then use up the client buffer. */
 	if (filter->client_avail > 0) {
 		min = minimum(request, (int64_t)filter->client_avail);
-		bytes_skipped = __archive_read_filter_consume(filter, min);
-		request -= bytes_skipped;
-		total_bytes_skipped += bytes_skipped;
+		filter->client_next += min;
+		filter->client_avail -= min;
+		request -= min;
+		filter->position += min;
+		total_bytes_skipped += min;
 	}
 	if (request == 0)
 		return (total_bytes_skipped);
-	/*
-	 * If a client_skipper was provided, try that first.
-	 */
-#if ARCHIVE_API_VERSION < 2
-	if ((filter->skip != NULL) && (request < SSIZE_MAX)) {
-#else
+
+	/* If there's an optimized skip function, use it. */
 	if (filter->skip != NULL) {
-#endif
 		bytes_skipped = (filter->skip)(filter, request);
 		if (bytes_skipped < 0) {	/* error */
-			filter->client_total = filter->client_avail = 0;
-			filter->client_next = filter->client_buff = NULL;
 			filter->fatal = 1;
 			return (bytes_skipped);
 		}
+		filter->position += bytes_skipped;
 		total_bytes_skipped += bytes_skipped;
 		request -= bytes_skipped;
-		filter->client_next = filter->client_buff;
-		filter->client_avail = filter->client_total = 0;
+		if (request == 0)
+			return (total_bytes_skipped);
 	}
-	/*
-	 * Note that client_skipper will usually not satisfy the
-	 * full request (due to low-level blocking concerns),
-	 * so even if client_skipper is provided, we may still
-	 * have to use ordinary reads to finish out the request.
-	 */
-	while (request > 0) {
-		ssize_t bytes_read;
-		(void)__archive_read_filter_ahead(filter, 1, &bytes_read);
-		if (bytes_read < 0)
+
+	/* Use ordinary reads as necessary to complete the request. */
+	for (;;) {
+		bytes_read = (filter->read)(filter, &filter->client_buff);
+		if (bytes_read < 0) {
+			filter->client_buff = NULL;
+			filter->fatal = 1;
 			return (bytes_read);
+		}
+
 		if (bytes_read == 0) {
+			filter->client_buff = NULL;
+			filter->end_of_file = 1;
 			return (total_bytes_skipped);
 		}
-		min = (size_t)(minimum(bytes_read, request));
-		bytes_read = __archive_read_filter_consume(filter, min);
+
+		if (bytes_read >= request) {
+			filter->client_next =
+			    ((const char *)filter->client_buff) + request;
+			filter->client_avail = bytes_read - request;
+			filter->client_total = bytes_read;
+			total_bytes_skipped += request;
+			filter->position += request;
+			return (total_bytes_skipped);
+		}
+
+		filter->position += bytes_read;
 		total_bytes_skipped += bytes_read;
 		request -= bytes_read;
 	}
-	return (total_bytes_skipped);
 }
+
+/**
+ * Returns ARCHIVE_FAILED if seeking isn't supported.
+ */
+int64_t
+__archive_read_seek(struct archive_read *a, int64_t offset, int whence)
+{
+	return __archive_read_filter_seek(a->filter, offset, whence);
+}
+
+int64_t
+__archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, int whence)
+{
+	int64_t r;
+
+	if (filter->closed || filter->fatal)
+		return (ARCHIVE_FATAL);
+	if (filter->seek == NULL)
+		return (ARCHIVE_FAILED);
+	r = filter->seek(filter, offset, whence);
+	if (r >= 0) {
+		/*
+		 * Ouch.  Clearing the buffer like this hurts, especially
+		 * at bid time.  A lot of our efficiency at bid time comes
+		 * from having bidders reuse the data we've already read.
+		 *
+		 * TODO: If the seek request is in data we already
+		 * have, then don't call the seek callback.
+		 *
+		 * TODO: Zip seeks to end-of-file at bid time.  If
+		 * other formats also start doing this, we may need to
+		 * find a way for clients to fudge the seek offset to
+		 * a block boundary.
+		 *
+		 * Hmmm... If whence was SEEK_END, we know the file
+		 * size is (r - offset).  Can we use that to simplify
+		 * the TODO items above?
+		 */
+		filter->avail = filter->client_avail = 0;
+		filter->next = filter->buffer;
+		filter->position = r;
+		filter->end_of_file = 0;
+	}
+	return r;
+}
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_data_into_fd.c
--- a/head/contrib/libarchive/libarchive/archive_read_data_into_fd.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_data_into_fd.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_data_into_fd.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_data_into_fd.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -45,31 +45,68 @@
 /*
  * This implementation minimizes copying of data and is sparse-file aware.
  */
+static int
+pad_to(struct archive *a, int fd, int can_lseek,
+    size_t nulls_size, const char *nulls,
+    int64_t target_offset, int64_t actual_offset)
+{
+	size_t to_write;
+	ssize_t bytes_written;
+
+	if (can_lseek) {
+		actual_offset = lseek(fd,
+		    target_offset - actual_offset, SEEK_CUR);
+		if (actual_offset != target_offset) {
+			archive_set_error(a, errno, "Seek error");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_OK);
+	}
+	while (target_offset > actual_offset) {
+		to_write = nulls_size;
+		if (target_offset < actual_offset + (int64_t)nulls_size)
+			to_write = (size_t)(target_offset - actual_offset);
+		bytes_written = write(fd, nulls, to_write);
+		if (bytes_written < 0) {
+			archive_set_error(a, errno, "Write error");
+			return (ARCHIVE_FATAL);
+		}
+		actual_offset += bytes_written;
+	}
+	return (ARCHIVE_OK);
+}
+
+
 int
 archive_read_data_into_fd(struct archive *a, int fd)
 {
-	int r;
+	struct stat st;
+	int r, r2;
 	const void *buff;
 	size_t size, bytes_to_write;
-	ssize_t bytes_written, total_written;
-	off_t offset;
-	off_t output_offset;
+	ssize_t bytes_written;
+	int64_t target_offset;
+	int64_t actual_offset = 0;
+	int can_lseek;
+	char *nulls = NULL;
+	size_t nulls_size = 16384;
 
-	__archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_into_fd");
+	archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_read_data_into_fd");
 
-	total_written = 0;
-	output_offset = 0;
+	can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode);
+	if (!can_lseek)
+		nulls = calloc(1, nulls_size);
 
-	while ((r = archive_read_data_block(a, &buff, &size, &offset)) ==
+	while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) ==
 	    ARCHIVE_OK) {
 		const char *p = buff;
-		if (offset > output_offset) {
-			output_offset = lseek(fd,
-			    offset - output_offset, SEEK_CUR);
-			if (output_offset != offset) {
-				archive_set_error(a, errno, "Seek error");
-				return (ARCHIVE_FATAL);
-			}
+		if (target_offset > actual_offset) {
+			r = pad_to(a, fd, can_lseek, nulls_size, nulls,
+			    target_offset, actual_offset);
+			if (r != ARCHIVE_OK)
+				break;
+			actual_offset = target_offset;
 		}
 		while (size > 0) {
 			bytes_to_write = size;
@@ -78,15 +115,24 @@
 			bytes_written = write(fd, p, bytes_to_write);
 			if (bytes_written < 0) {
 				archive_set_error(a, errno, "Write error");
-				return (ARCHIVE_FATAL);
+				r = ARCHIVE_FATAL;
+				goto cleanup;
 			}
-			output_offset += bytes_written;
-			total_written += bytes_written;
+			actual_offset += bytes_written;
 			p += bytes_written;
 			size -= bytes_written;
 		}
 	}
 
+	if (r == ARCHIVE_EOF && target_offset > actual_offset) {
+		r2 = pad_to(a, fd, can_lseek, nulls_size, nulls,
+		    target_offset, actual_offset);
+		if (r2 != ARCHIVE_OK)
+			r = r2;
+	}
+
+cleanup:
+	free(nulls);
 	if (r != ARCHIVE_EOF)
 		return (r);
 	return (ARCHIVE_OK);
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_disk.3
--- a/head/contrib/libarchive/libarchive/archive_read_disk.3	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_disk.3	Fri Mar 02 16:54:40 2012 +0200
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: head/contrib/libarchive/libarchive/archive_read_disk.3 228773 2011-12-21 15:18:52Z mm $
+.\" $FreeBSD: head/contrib/libarchive/libarchive/archive_read_disk.3 232153 2012-02-25 10:58:02Z mm $
 .\"
 .Dd March 10, 2009
 .Dt ARCHIVE_READ_DISK 3
@@ -39,6 +39,7 @@
 .Nm archive_read_disk_set_gname_lookup ,
 .Nm archive_read_disk_set_standard_lookup ,
 .Nm archive_read_close ,
+.Nm archive_read_finish ,
 .Nm archive_read_free
 .Nd functions for reading objects from disk
 .Sh SYNOPSIS
@@ -81,6 +82,8 @@
 .Ft int
 .Fn archive_read_close "struct archive *"
 .Ft int
+.Fn archive_read_finish "struct archive *"
+.Ft int
 .Fn archive_read_free "struct archive *"
 .Sh DESCRIPTION
 These functions provide an API for reading information about
@@ -177,7 +180,12 @@
 .Tn struct archive_entry
 object.
 .It Fn archive_read_close
-This currently does nothing.
+Does nothing for
+.Tn archive_read_disk
+handles.
+.It Fn archive_read_finish
+This is a deprecated synonym for
+.Fn archive_read_free .
 .It Fn archive_read_free
 Invokes
 .Fn archive_read_close
@@ -207,7 +215,7 @@
   fd = open(name, O_RDONLY);
   if (fd < 0)
      return;
-  archive_entry_copy_sourcepath(entry, name);
+  archive_entry_copy_pathname(entry, name);
   archive_read_disk_entry_from_file(ard, entry, fd, NULL);
   archive_write_header(a, entry);
   while ((bytes_read = read(fd, buff, sizeof(buff))) > 0)
@@ -229,15 +237,6 @@
 for unusual conditions that do not prevent further operations, and
 .Cm ARCHIVE_FATAL
 for serious errors that make remaining operations impossible.
-The
-.Xr archive_errno 3
-and
-.Xr archive_error_string 3
-functions can be used to retrieve an appropriate error code and a
-textual error message.
-(See
-.Xr archive_util 3
-for details.)
 .Pp
 .Fn archive_read_disk_new
 returns a pointer to a newly-allocated
@@ -253,9 +252,17 @@
 The returned pointer points to internal storage that
 may be reused on the next call to either of these functions;
 callers should copy the string if they need to continue accessing it.
-.Pp
+.\"
+.Sh ERRORS
+Detailed error codes and textual descriptions are available from the
+.Fn archive_errno
+and
+.Fn archive_error_string
+functions.
+.\"
 .Sh SEE ALSO
 .Xr archive_read 3 ,
+.Xr archive_util 3 ,
 .Xr archive_write 3 ,
 .Xr archive_write_disk 3 ,
 .Xr tar 1 ,
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c
--- a/head/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,7 +25,10 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c 232153 2012-02-25 10:58:02Z mm $");
+
+/* This is the tree-walking code for POSIX systems. */
+#if !defined(_WIN32) || defined(__CYGWIN__)
 
 #ifdef HAVE_SYS_TYPES_H
 /* Mac OSX requires sys/types.h before sys/acl.h. */
@@ -36,6 +40,9 @@
 #ifdef HAVE_SYS_EXTATTR_H
 #include <sys/extattr.h>
 #endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
 #ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
 #endif
@@ -45,20 +52,48 @@
 #ifdef HAVE_SYS_XATTR_H
 #include <sys/xattr.h>
 #endif
+#ifdef HAVE_SYS_EA_H
+#include <sys/ea.h>
+#endif
 #ifdef HAVE_ACL_LIBACL_H
 #include <acl/libacl.h>
 #endif
 #ifdef HAVE_ATTR_XATTR_H
 #include <attr/xattr.h>
 #endif
+#ifdef HAVE_COPYFILE_H
+#include <copyfile.h>
+#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
 #ifdef HAVE_LIMITS_H
 #include <limits.h>
 #endif
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
+#ifdef HAVE_LINUX_FIEMAP_H
+#include <linux/fiemap.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#include <linux/ext2_fs.h>      /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */
+#endif
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
 #endif
 
 #include "archive.h"
@@ -78,13 +113,18 @@
 
 static int setup_acls_posix1e(struct archive_read_disk *,
     struct archive_entry *, int fd);
+static int setup_mac_metadata(struct archive_read_disk *,
+    struct archive_entry *, int fd);
 static int setup_xattrs(struct archive_read_disk *,
     struct archive_entry *, int fd);
+static int setup_sparse(struct archive_read_disk *,
+    struct archive_entry *, int fd);
 
 int
 archive_read_disk_entry_from_file(struct archive *_a,
     struct archive_entry *entry,
-    int fd, const struct stat *st)
+    int fd,
+    const struct stat *st)
 {
 	struct archive_read_disk *a = (struct archive_read_disk *)_a;
 	const char *path, *name;
@@ -97,54 +137,35 @@
 	if (path == NULL)
 		path = archive_entry_pathname(entry);
 
-#ifdef EXT2_IOC_GETFLAGS
-	/* Linux requires an extra ioctl to pull the flags.  Although
-	 * this is an extra step, it has a nice side-effect: We get an
-	 * open file descriptor which we can use in the subsequent lookups. */
-	if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
-		if (fd < 0)
-			fd = open(pathname, O_RDONLY | O_NONBLOCK | O_BINARY);
-		if (fd >= 0) {
-			unsigned long stflags;
-			int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
-			if (r == 0 && stflags != 0)
-				archive_entry_set_fflags(entry, stflags, 0);
-		}
-	}
+	if (a->tree == NULL) {
+		if (st == NULL) {
+#if HAVE_FSTAT
+			if (fd >= 0) {
+				if (fstat(fd, &s) != 0) {
+					archive_set_error(&a->archive, errno,
+					    "Can't fstat");
+					return (ARCHIVE_FAILED);
+				}
+			} else
 #endif
-
-	if (st == NULL) {
-		/* TODO: On Windows, use GetFileInfoByHandle() here.
-		 * Using Windows stat() call is badly broken, but
-		 * even the stat() wrapper has problems because
-		 * 'struct stat' is broken on Windows.
-		 */
-#if HAVE_FSTAT
-		if (fd >= 0) {
-			if (fstat(fd, &s) != 0) {
+#if HAVE_LSTAT
+			if (!a->follow_symlinks) {
+				if (lstat(path, &s) != 0) {
+					archive_set_error(&a->archive, errno,
+					    "Can't lstat %s", path);
+					return (ARCHIVE_FAILED);
+				}
+			} else
+#endif
+			if (stat(path, &s) != 0) {
 				archive_set_error(&a->archive, errno,
-				    "Can't fstat");
+				    "Can't stat %s", path);
 				return (ARCHIVE_FAILED);
 			}
-		} else
-#endif
-#if HAVE_LSTAT
-		if (!a->follow_symlinks) {
-			if (lstat(path, &s) != 0) {
-				archive_set_error(&a->archive, errno,
-				    "Can't lstat %s", path);
-				return (ARCHIVE_FAILED);
-			}
-		} else
-#endif
-		if (stat(path, &s) != 0) {
-			archive_set_error(&a->archive, errno,
-			    "Can't lstat %s", path);
-			return (ARCHIVE_FAILED);
+			st = &s;
 		}
-		st = &s;
+		archive_entry_copy_stat(entry, st);
 	}
-	archive_entry_copy_stat(entry, st);
 
 	/* Lookup uname/gname */
 	name = archive_read_disk_uname(_a, archive_entry_uid(entry));
@@ -161,30 +182,185 @@
 		archive_entry_set_fflags(entry, st->st_flags, 0);
 #endif
 
-#ifdef HAVE_READLINK
+#if defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)
+	/* Linux requires an extra ioctl to pull the flags.  Although
+	 * this is an extra step, it has a nice side-effect: We get an
+	 * open file descriptor which we can use in the subsequent lookups. */
+	if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
+		if (fd < 0)
+			fd = open(path, O_RDONLY | O_NONBLOCK);
+		if (fd >= 0) {
+			unsigned long stflags;
+			r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
+			if (r == 0 && stflags != 0)
+				archive_entry_set_fflags(entry, stflags, 0);
+		}
+	}
+#endif
+
+#if defined(HAVE_READLINK) || defined(HAVE_READLINKAT)
 	if (S_ISLNK(st->st_mode)) {
-		char linkbuffer[PATH_MAX + 1];
-		int lnklen = readlink(path, linkbuffer, PATH_MAX);
+		size_t linkbuffer_len = st->st_size + 1;
+		char *linkbuffer;
+		int lnklen;
+
+		linkbuffer = malloc(linkbuffer_len);
+		if (linkbuffer == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Couldn't read link data");
+			return (ARCHIVE_FAILED);
+		}
+#ifdef HAVE_READLINKAT
+		if (a->entry_wd_fd >= 0)
+			lnklen = readlinkat(a->entry_wd_fd, path,
+			    linkbuffer, linkbuffer_len);
+		else
+#endif /* HAVE_READLINKAT */
+		lnklen = readlink(path, linkbuffer, linkbuffer_len);
 		if (lnklen < 0) {
 			archive_set_error(&a->archive, errno,
 			    "Couldn't read link data");
+			free(linkbuffer);
 			return (ARCHIVE_FAILED);
 		}
 		linkbuffer[lnklen] = 0;
 		archive_entry_set_symlink(entry, linkbuffer);
+		free(linkbuffer);
 	}
-#endif
+#endif /* HAVE_READLINK || HAVE_READLINKAT */
 
 	r = setup_acls_posix1e(a, entry, fd);
 	r1 = setup_xattrs(a, entry, fd);
 	if (r1 < r)
 		r = r1;
+	r1 = setup_mac_metadata(a, entry, fd);
+	if (r1 < r)
+		r = r1;
+	r1 = setup_sparse(a, entry, fd);
+	if (r1 < r)
+		r = r1;
+
 	/* If we opened the file earlier in this function, close it. */
 	if (initial_fd != fd)
 		close(fd);
 	return (r);
 }
 
+#if defined(__APPLE__) && defined(HAVE_COPYFILE_H)
+/*
+ * The Mac OS "copyfile()" API copies the extended metadata for a
+ * file into a separate file in AppleDouble format (see RFC 1740).
+ *
+ * Mac OS tar and cpio implementations store this extended
+ * metadata as a separate entry just before the regular entry
+ * with a "._" prefix added to the filename.
+ *
+ * Note that this is currently done unconditionally; the tar program has
+ * an option to discard this information before the archive is written.
+ *
+ * TODO: If there's a failure, report it and return ARCHIVE_WARN.
+ */
+static int
+setup_mac_metadata(struct archive_read_disk *a,
+    struct archive_entry *entry, int fd)
+{
+	int tempfd = -1;
+	int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR;
+	struct stat copyfile_stat;
+	int ret = ARCHIVE_OK;
+	void *buff;
+	int have_attrs;
+	const char *name, *tempdir, *tempfile = NULL;
+
+	name = archive_entry_sourcepath(entry);
+	if (name == NULL)
+		name = archive_entry_pathname(entry);
+	if (name == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Can't open file to read extended attributes: No name");
+		return (ARCHIVE_WARN);
+	}
+
+	/* Short-circuit if there's nothing to do. */
+	have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK);
+	if (have_attrs == -1) {
+		archive_set_error(&a->archive, errno,
+			"Could not check extended attributes");
+		return (ARCHIVE_WARN);
+	}
+	if (have_attrs == 0)
+		return (ARCHIVE_OK);
+
+	tempdir = NULL;
+	if (issetugid() == 0)
+		tempdir = getenv("TMPDIR");
+	if (tempdir == NULL)
+		tempdir = _PATH_TMP;
+	tempfile = tempnam(tempdir, "tar.md.");
+
+	/* XXX I wish copyfile() could pack directly to a memory
+	 * buffer; that would avoid the temp file here.  For that
+	 * matter, it would be nice if fcopyfile() actually worked,
+	 * that would reduce the many open/close races here. */
+	if (copyfile(name, tempfile, 0, copyfile_flags | COPYFILE_PACK)) {
+		archive_set_error(&a->archive, errno,
+		    "Could not pack extended attributes");
+		ret = ARCHIVE_WARN;
+		goto cleanup;
+	}
+	tempfd = open(tempfile, O_RDONLY);
+	if (tempfd < 0) {
+		archive_set_error(&a->archive, errno,
+		    "Could not open extended attribute file");
+		ret = ARCHIVE_WARN;
+		goto cleanup;
+	}
+	if (fstat(tempfd, &copyfile_stat)) {
+		archive_set_error(&a->archive, errno,
+		    "Could not check size of extended attributes");
+		ret = ARCHIVE_WARN;
+		goto cleanup;
+	}
+	buff = malloc(copyfile_stat.st_size);
+	if (buff == NULL) {
+		archive_set_error(&a->archive, errno,
+		    "Could not allocate memory for extended attributes");
+		ret = ARCHIVE_WARN;
+		goto cleanup;
+	}
+	if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) {
+		archive_set_error(&a->archive, errno,
+		    "Could not read extended attributes into memory");
+		ret = ARCHIVE_WARN;
+		goto cleanup;
+	}
+	archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size);
+
+cleanup:
+	if (tempfd >= 0)
+		close(tempfd);
+	if (tempfile != NULL)
+		unlink(tempfile);
+	return (ret);
+}
+
+#else
+
+/*
+ * Stub implementation for non-Mac systems.
+ */
+static int
+setup_mac_metadata(struct archive_read_disk *a,
+    struct archive_entry *entry, int fd)
+{
+	(void)a; /* UNUSED */
+	(void)entry; /* UNUSED */
+	(void)fd; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+#endif
+
+
 #ifdef HAVE_POSIX_ACL
 static void setup_acl_posix1e(struct archive_read_disk *a,
     struct archive_entry *entry, acl_t acl, int archive_entry_acl_type);
@@ -307,10 +483,12 @@
 }
 #endif
 
-#if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR
+#if (HAVE_FGETXATTR && HAVE_FLISTXATTR && HAVE_LISTXATTR && \
+    HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR) || \
+    (HAVE_FGETEA && HAVE_FLISTEA && HAVE_LISTEA)
 
 /*
- * Linux extended attribute support.
+ * Linux and AIX extended attribute support.
  *
  * TODO:  By using a stack-allocated buffer for the first
  * call to getxattr(), we might be able to avoid the second
@@ -329,16 +507,25 @@
 	void *value = NULL;
 	const char *accpath;
 
-	(void)fd; /* UNUSED */
-
 	accpath = archive_entry_sourcepath(entry);
 	if (accpath == NULL)
 		accpath = archive_entry_pathname(entry);
 
-	if (!a->follow_symlinks)
+#if HAVE_FGETXATTR
+	if (fd >= 0)
+		size = fgetxattr(fd, name, NULL, 0);
+	else if (!a->follow_symlinks)
 		size = lgetxattr(accpath, name, NULL, 0);
 	else
 		size = getxattr(accpath, name, NULL, 0);
+#elif HAVE_FGETEA
+	if (fd >= 0)
+		size = fgetea(fd, name, NULL, 0);
+	else if (!a->follow_symlinks)
+		size = lgetea(accpath, name, NULL, 0);
+	else
+		size = getea(accpath, name, NULL, 0);
+#endif
 
 	if (size == -1) {
 		archive_set_error(&a->archive, errno,
@@ -351,10 +538,21 @@
 		return (ARCHIVE_FATAL);
 	}
 
-	if (!a->follow_symlinks)
+#if HAVE_FGETXATTR
+	if (fd >= 0)
+		size = fgetxattr(fd, name, value, size);
+	else if (!a->follow_symlinks)
 		size = lgetxattr(accpath, name, value, size);
 	else
 		size = getxattr(accpath, name, value, size);
+#elif HAVE_FGETEA
+	if (fd >= 0)
+		size = fgetea(fd, name, value, size);
+	else if (!a->follow_symlinks)
+		size = lgetea(accpath, name, value, size);
+	else
+		size = getea(accpath, name, value, size);
+#endif
 
 	if (size == -1) {
 		archive_set_error(&a->archive, errno,
@@ -376,18 +574,28 @@
 	const char *path;
 	ssize_t list_size;
 
-
 	path = archive_entry_sourcepath(entry);
 	if (path == NULL)
 		path = archive_entry_pathname(entry);
 
-	if (!a->follow_symlinks)
+#if HAVE_FLISTXATTR
+	if (fd >= 0)
+		list_size = flistxattr(fd, NULL, 0);
+	else if (!a->follow_symlinks)
 		list_size = llistxattr(path, NULL, 0);
 	else
 		list_size = listxattr(path, NULL, 0);
+#elif HAVE_FLISTEA
+	if (fd >= 0)
+		list_size = flistea(fd, NULL, 0);
+	else if (!a->follow_symlinks)
+		list_size = llistea(path, NULL, 0);
+	else
+		list_size = listea(path, NULL, 0);
+#endif
 
 	if (list_size == -1) {
-		if (errno == ENOTSUP)
+		if (errno == ENOTSUP || errno == ENOSYS)
 			return (ARCHIVE_OK);
 		archive_set_error(&a->archive, errno,
 			"Couldn't list extended attributes");
@@ -402,10 +610,21 @@
 		return (ARCHIVE_FATAL);
 	}
 
-	if (!a->follow_symlinks)
+#if HAVE_FLISTXATTR
+	if (fd >= 0)
+		list_size = flistxattr(fd, list, list_size);
+	else if (!a->follow_symlinks)
 		list_size = llistxattr(path, list, list_size);
 	else
 		list_size = listxattr(path, list, list_size);
+#elif HAVE_FLISTEA
+	if (fd >= 0)
+		list_size = flistea(fd, list, list_size);
+	else if (!a->follow_symlinks)
+		list_size = llistea(path, list, list_size);
+	else
+		list_size = listea(path, list, list_size);
+#endif
 
 	if (list_size == -1) {
 		archive_set_error(&a->archive, errno,
@@ -449,13 +668,13 @@
 	void *value = NULL;
 	const char *accpath;
 
-	(void)fd; /* UNUSED */
-
 	accpath = archive_entry_sourcepath(entry);
 	if (accpath == NULL)
 		accpath = archive_entry_pathname(entry);
 
-	if (!a->follow_symlinks)
+	if (fd >= 0)
+		size = extattr_get_fd(fd, namespace, name, NULL, 0);
+	else if (!a->follow_symlinks)
 		size = extattr_get_link(accpath, namespace, name, NULL, 0);
 	else
 		size = extattr_get_file(accpath, namespace, name, NULL, 0);
@@ -471,7 +690,9 @@
 		return (ARCHIVE_FATAL);
 	}
 
-	if (!a->follow_symlinks)
+	if (fd >= 0)
+		size = extattr_get_fd(fd, namespace, name, value, size);
+	else if (!a->follow_symlinks)
 		size = extattr_get_link(accpath, namespace, name, value, size);
 	else
 		size = extattr_get_file(accpath, namespace, name, value, size);
@@ -502,7 +723,9 @@
 	if (path == NULL)
 		path = archive_entry_pathname(entry);
 
-	if (!a->follow_symlinks)
+	if (fd >= 0)
+		list_size = extattr_list_fd(fd, namespace, NULL, 0);
+	else if (!a->follow_symlinks)
 		list_size = extattr_list_link(path, namespace, NULL, 0);
 	else
 		list_size = extattr_list_file(path, namespace, NULL, 0);
@@ -523,7 +746,9 @@
 		return (ARCHIVE_FATAL);
 	}
 
-	if (!a->follow_symlinks)
+	if (fd >= 0)
+		list_size = extattr_list_fd(fd, namespace, list, list_size);
+	else if (!a->follow_symlinks)
 		list_size = extattr_list_link(path, namespace, list, list_size);
 	else
 		list_size = extattr_list_file(path, namespace, list, list_size);
@@ -568,3 +793,213 @@
 }
 
 #endif
+
+#if defined(HAVE_LINUX_FIEMAP_H)
+
+/*
+ * Linux sparse interface.
+ *
+ * The FIEMAP ioctl returns an "extent" for each physical allocation
+ * on disk.  We need to process those to generate a more compact list
+ * of logical file blocks.  We also need to be very careful to use
+ * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes
+ * does not report allocations for newly-written data that hasn't
+ * been synced to disk.
+ *
+ * It's important to return a minimal sparse file list because we want
+ * to not trigger sparse file extensions if we don't have to, since
+ * not all readers support them.
+ */
+
+static int
+setup_sparse(struct archive_read_disk *a,
+    struct archive_entry *entry, int fd)
+{
+	char buff[4096];
+	struct fiemap *fm;
+	struct fiemap_extent *fe;
+	int64_t size;
+	int count, do_fiemap;
+	int initial_fd = fd;
+	int exit_sts = ARCHIVE_OK;
+
+	if (archive_entry_filetype(entry) != AE_IFREG
+	    || archive_entry_size(entry) <= 0
+	    || archive_entry_hardlink(entry) != NULL)
+		return (ARCHIVE_OK);
+
+	if (fd < 0) {
+		const char *path;
+
+		path = archive_entry_sourcepath(entry);
+		if (path == NULL)
+			path = archive_entry_pathname(entry);
+		fd = open(path, O_RDONLY | O_NONBLOCK);
+		if (fd < 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't open `%s'", path);
+			return (ARCHIVE_FAILED);
+		}
+	}
+
+	count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe);
+	fm = (struct fiemap *)buff;
+	fm->fm_start = 0;
+	fm->fm_length = ~0ULL;;
+	fm->fm_flags = FIEMAP_FLAG_SYNC;
+	fm->fm_extent_count = count;
+	do_fiemap = 1;
+	size = archive_entry_size(entry);
+	for (;;) {
+		int i, r;
+
+		r = ioctl(fd, FS_IOC_FIEMAP, fm); 
+		if (r < 0) {
+			/* When errno is ENOTTY, it is better we should
+			 * return ARCHIVE_OK because an earlier version
+			 *(<2.6.28) cannot perfom FS_IOC_FIEMAP.
+			 * We should also check if errno is EOPNOTSUPP,
+			 * it means "Operation not supported". */
+			if (errno != ENOTTY && errno != EOPNOTSUPP) {
+				archive_set_error(&a->archive, errno,
+				    "FIEMAP failed");
+				exit_sts = ARCHIVE_FAILED;
+			}
+			goto exit_setup_sparse;
+		}
+		if (fm->fm_mapped_extents == 0)
+			break;
+		fe = fm->fm_extents;
+		for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) {
+			if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
+				/* The fe_length of the last block does not
+				 * adjust itself to its size files. */
+				int64_t length = fe->fe_length;
+				if (fe->fe_logical + length > (uint64_t)size)
+					length -= fe->fe_logical + length - size;
+				if (fe->fe_logical == 0 && length == size) {
+					/* This is not sparse. */
+					do_fiemap = 0;
+					break;
+				}
+				if (length > 0)
+					archive_entry_sparse_add_entry(entry,
+					    fe->fe_logical, length);
+			}
+			if (fe->fe_flags & FIEMAP_EXTENT_LAST)
+				do_fiemap = 0;
+		}
+		if (do_fiemap) {
+			fe = fm->fm_extents + fm->fm_mapped_extents -1;
+			fm->fm_start = fe->fe_logical + fe->fe_length;
+		} else
+			break;
+	}
+exit_setup_sparse:
+	if (initial_fd != fd)
+		close(fd);
+	return (exit_sts);
+}
+
+#elif defined(SEEK_HOLE) && defined(SEEK_DATA) && defined(_PC_MIN_HOLE_SIZE)
+
+/*
+ * FreeBSD and Solaris sparse interface.
+ */
+
+static int
+setup_sparse(struct archive_read_disk *a,
+    struct archive_entry *entry, int fd)
+{
+	int64_t size;
+	int initial_fd = fd;
+	off_t initial_off; /* FreeBSD/Solaris only, so off_t okay here */
+	off_t off_s, off_e; /* FreeBSD/Solaris only, so off_t okay here */
+	int exit_sts = ARCHIVE_OK;
+
+	if (archive_entry_filetype(entry) != AE_IFREG
+	    || archive_entry_size(entry) <= 0
+	    || archive_entry_hardlink(entry) != NULL)
+		return (ARCHIVE_OK);
+
+	/* Does filesystem support the reporting of hole ? */
+	if (fd >= 0) {
+		if (fpathconf(fd, _PC_MIN_HOLE_SIZE) <= 0)
+			return (ARCHIVE_OK);
+		initial_off = lseek(fd, 0, SEEK_CUR);
+		if (initial_off != 0)
+			lseek(fd, 0, SEEK_SET);
+	} else {
+		const char *path;
+
+		path = archive_entry_sourcepath(entry);
+		if (path == NULL)
+			path = archive_entry_pathname(entry);
+		if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0)
+			return (ARCHIVE_OK);
+		fd = open(path, O_RDONLY | O_NONBLOCK);
+		if (fd < 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't open `%s'", path);
+			return (ARCHIVE_FAILED);
+		}
+		initial_off = 0;
+	}
+
+	off_s = 0;
+	size = archive_entry_size(entry);
+	while (off_s < size) {
+		off_s = lseek(fd, off_s, SEEK_DATA);
+		if (off_s == (off_t)-1) {
+			if (errno == ENXIO)
+				break;/* no more hole */
+			archive_set_error(&a->archive, errno,
+			    "lseek(SEEK_HOLE) failed");
+			exit_sts = ARCHIVE_FAILED;
+			goto exit_setup_sparse;
+		}
+		off_e = lseek(fd, off_s, SEEK_HOLE);
+		if (off_s == (off_t)-1) {
+			if (errno == ENXIO) {
+				off_e = lseek(fd, 0, SEEK_END);
+				if (off_e != (off_t)-1)
+					break;/* no more data */
+			}
+			archive_set_error(&a->archive, errno,
+			    "lseek(SEEK_DATA) failed");
+			exit_sts = ARCHIVE_FAILED;
+			goto exit_setup_sparse;
+		}
+		if (off_s == 0 && off_e == size)
+			break;/* This is not spase. */
+		archive_entry_sparse_add_entry(entry, off_s,
+			off_e - off_s);
+		off_s = off_e;
+	}
+exit_setup_sparse:
+	if (initial_fd != fd)
+		close(fd);
+	else
+		lseek(fd, initial_off, SEEK_SET);
+	return (exit_sts);
+}
+
+#else
+
+/*
+ * Generic (stub) sparse support.
+ */
+static int
+setup_sparse(struct archive_read_disk *a,
+    struct archive_entry *entry, int fd)
+{
+	(void)a;     /* UNUSED */
+	(void)entry; /* UNUSED */
+	(void)fd;    /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+#endif
+
+#endif /* !defined(_WIN32) || defined(__CYGWIN__) */
+
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_disk_private.h
--- a/head/contrib/libarchive/libarchive/archive_read_disk_private.h	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_disk_private.h	Fri Mar 02 16:54:40 2012 +0200
@@ -23,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/contrib/libarchive/libarchive/archive_read_disk_private.h 228763 2011-12-21 11:13:29Z mm $
+ * $FreeBSD: head/contrib/libarchive/libarchive/archive_read_disk_private.h 232153 2012-02-25 10:58:02Z mm $
  */
 
 #ifndef __LIBARCHIVE_BUILD
@@ -33,6 +33,8 @@
 #ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
 #define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
 
+struct tree;
+
 struct archive_read_disk {
 	struct archive	archive;
 
@@ -51,10 +53,17 @@
 	 */
 	char	follow_symlinks;  /* Either 'L' or 'P'. */
 
-	const char * (*lookup_gname)(void *private, gid_t gid);
+	/* Directory traversals. */
+	struct tree *tree;
+
+	/* Set 1 if users request to restore atime . */
+	int		 restore_time;
+	int		 entry_wd_fd;
+
+	const char * (*lookup_gname)(void *private, int64_t gid);
 	void	(*cleanup_gname)(void *private);
 	void	 *lookup_gname_data;
-	const char * (*lookup_uname)(void *private, gid_t gid);
+	const char * (*lookup_uname)(void *private, int64_t uid);
 	void	(*cleanup_uname)(void *private);
 	void	 *lookup_uname_data;
 };
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c
--- a/head/contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -72,8 +72,8 @@
 	} cache[name_cache_size];
 };
 
-static const char *	lookup_gname(void *, gid_t);
-static const char *	lookup_uname(void *, uid_t);
+static const char *	lookup_gname(void *, int64_t);
+static const char *	lookup_uname(void *, int64_t);
 static void	cleanup(void *);
 static const char *	lookup_gname_helper(struct name_cache *, id_t gid);
 static const char *	lookup_uname_helper(struct name_cache *, id_t uid);
@@ -175,7 +175,7 @@
 }
 
 static const char *
-lookup_uname(void *data, uid_t uid)
+lookup_uname(void *data, int64_t uid)
 {
 	struct name_cache *uname_cache = (struct name_cache *)data;
 	return (lookup_name(uname_cache,
@@ -187,6 +187,8 @@
 lookup_uname_helper(struct name_cache *cache, id_t id)
 {
 	struct passwd	pwent, *result;
+	char * nbuff;
+	size_t nbuff_size;
 	int r;
 
 	if (cache->buff_size == 0) {
@@ -208,10 +210,12 @@
 		 * we just double it and try again.  Because the buffer
 		 * is kept around in the cache object, we shouldn't
 		 * have to do this very often. */
-		cache->buff_size *= 2;
-		cache->buff = realloc(cache->buff, cache->buff_size);
-		if (cache->buff == NULL)
+		nbuff_size = cache->buff_size * 2;
+		nbuff = realloc(cache->buff, nbuff_size);
+		if (nbuff == NULL)
 			break;
+		cache->buff = nbuff;
+		cache->buff_size = nbuff_size;
 	}
 	if (r != 0) {
 		archive_set_error(cache->archive, errno,
@@ -239,7 +243,7 @@
 #endif
 
 static const char *
-lookup_gname(void *data, gid_t gid)
+lookup_gname(void *data, int64_t gid)
 {
 	struct name_cache *gname_cache = (struct name_cache *)data;
 	return (lookup_name(gname_cache,
@@ -251,6 +255,8 @@
 lookup_gname_helper(struct name_cache *cache, id_t id)
 {
 	struct group	grent, *result;
+	char * nbuff;
+	size_t nbuff_size;
 	int r;
 
 	if (cache->buff_size == 0) {
@@ -270,10 +276,12 @@
 		/* ERANGE means our buffer was too small, but POSIX
 		 * doesn't tell us how big the buffer should be, so
 		 * we just double it and try again. */
-		cache->buff_size *= 2;
-		cache->buff = realloc(cache->buff, cache->buff_size);
-		if (cache->buff == NULL)
+		nbuff_size = cache->buff_size * 2;
+		nbuff = realloc(cache->buff, nbuff_size);
+		if (nbuff == NULL)
 			break;
+		cache->buff = nbuff;
+		cache->buff_size = nbuff_size;
 	}
 	if (r != 0) {
 		archive_set_error(cache->archive, errno,
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_extract.c
--- a/head/contrib/libarchive/libarchive/archive_read_extract.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_extract.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_extract.c 228773 2011-12-21 15:18:52Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_extract.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -100,8 +100,9 @@
 	int r, r2;
 
 	/* Set up for this particular entry. */
-	archive_write_disk_set_skip_file(ad,
-	    a->skip_file_dev, a->skip_file_ino);
+	if (a->skip_file_set)
+		archive_write_disk_set_skip_file(ad,
+		    a->skip_file_dev, a->skip_file_ino);
 	r = archive_write_header(ad, entry);
 	if (r < ARCHIVE_WARN)
 		r = ARCHIVE_WARN;
@@ -116,7 +117,7 @@
 		r2 = ARCHIVE_WARN;
 	/* Use the first message. */
 	if (r2 != ARCHIVE_OK && r == ARCHIVE_OK)
- 		archive_copy_error(&a->archive, ad);
+		archive_copy_error(&a->archive, ad);
 	/* Use the worst error return. */
 	if (r2 < r)
 		r = r2;
@@ -138,13 +139,15 @@
 static int
 copy_data(struct archive *ar, struct archive *aw)
 {
-	off_t offset;
+	int64_t offset;
 	const void *buff;
 	struct extract *extract;
 	size_t size;
 	int r;
 
 	extract = get_extract((struct archive_read *)ar);
+	if (extract == NULL)
+		return (ARCHIVE_FATAL);
 	for (;;) {
 		r = archive_read_data_block(ar, &buff, &size, &offset);
 		if (r == ARCHIVE_EOF)
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_open_fd.c
--- a/head/contrib/libarchive/libarchive/archive_read_open_fd.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_open_fd.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_open_fd.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_open_fd.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -53,17 +53,13 @@
 struct read_fd_data {
 	int	 fd;
 	size_t	 block_size;
-	char	 can_skip;
+	char	 use_lseek;
 	void	*buffer;
 };
 
 static int	file_close(struct archive *, void *);
 static ssize_t	file_read(struct archive *, void *, const void **buff);
-#if ARCHIVE_API_VERSION < 2
-static ssize_t	file_skip(struct archive *, void *, size_t request);
-#else
-static off_t	file_skip(struct archive *, void *, off_t request);
-#endif
+static int64_t	file_skip(struct archive *, void *, int64_t request);
 
 int
 archive_read_open_fd(struct archive *a, int fd, size_t block_size)
@@ -78,7 +74,7 @@
 		return (ARCHIVE_FATAL);
 	}
 
-	mine = (struct read_fd_data *)malloc(sizeof(*mine));
+	mine = (struct read_fd_data *)calloc(1, sizeof(*mine));
 	b = malloc(block_size);
 	if (mine == NULL || b == NULL) {
 		archive_set_error(a, ENOMEM, "No memory");
@@ -98,15 +94,17 @@
 	 */
 	if (S_ISREG(st.st_mode)) {
 		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
-		mine->can_skip = 1;
-	} else
-		mine->can_skip = 0;
+		mine->use_lseek = 1;
+	}
 #if defined(__CYGWIN__) || defined(_WIN32)
 	setmode(mine->fd, O_BINARY);
 #endif
 
-	return (archive_read_open2(a, mine,
-		NULL, file_read, file_skip, file_close));
+	archive_read_set_read_callback(a, file_read);
+	archive_read_set_skip_callback(a, file_skip);
+	archive_read_set_close_callback(a, file_close);
+	archive_read_set_callback_data(a, mine);
+	return (archive_read_open1(a));
 }
 
 static ssize_t
@@ -127,55 +125,48 @@
 	}
 }
 
-#if ARCHIVE_API_VERSION < 2
-static ssize_t
-file_skip(struct archive *a, void *client_data, size_t request)
-#else
-static off_t
-file_skip(struct archive *a, void *client_data, off_t request)
-#endif
+static int64_t
+file_skip(struct archive *a, void *client_data, int64_t request)
 {
 	struct read_fd_data *mine = (struct read_fd_data *)client_data;
+	off_t skip = (off_t)request;
 	off_t old_offset, new_offset;
+	int skip_bits = sizeof(skip) * 8 - 1;  /* off_t is a signed type. */
 
-	if (!mine->can_skip)
+	if (!mine->use_lseek)
 		return (0);
 
+	/* Reduce a request that would overflow the 'skip' variable. */
+	if (sizeof(request) > sizeof(skip)) {
+		int64_t max_skip =
+		    (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;
+		if (request > max_skip)
+			skip = max_skip;
+	}
+
 	/* Reduce request to the next smallest multiple of block_size */
 	request = (request / mine->block_size) * mine->block_size;
 	if (request == 0)
 		return (0);
 
+	if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0) &&
+	    ((new_offset = lseek(mine->fd, skip, SEEK_CUR)) >= 0))
+		return (new_offset - old_offset);
+
+	/* If seek failed once, it will probably fail again. */
+	mine->use_lseek = 0;
+
+	/* Let libarchive recover with read+discard. */
+	if (errno == ESPIPE)
+		return (0);
+
 	/*
-	 * Hurray for lazy evaluation: if the first lseek fails, the second
-	 * one will not be executed.
+	 * There's been an error other than ESPIPE. This is most
+	 * likely caused by a programmer error (too large request)
+	 * or a corrupted archive file.
 	 */
-	if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) ||
-	    ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0))
-	{
-		/* If seek failed once, it will probably fail again. */
-		mine->can_skip = 0;
-
-		if (errno == ESPIPE)
-		{
-			/*
-			 * Failure to lseek() can be caused by the file
-			 * descriptor pointing to a pipe, socket or FIFO.
-			 * Return 0 here, so the compression layer will use
-			 * read()s instead to advance the file descriptor.
-			 * It's slower of course, but works as well.
-			 */
-			return (0);
-		}
-		/*
-		 * There's been an error other than ESPIPE. This is most
-		 * likely caused by a programmer error (too large request)
-		 * or a corrupted archive file.
-		 */
-		archive_set_error(a, errno, "Error seeking");
-		return (-1);
-	}
-	return (new_offset - old_offset);
+	archive_set_error(a, errno, "Error seeking");
+	return (-1);
 }
 
 static int
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_open_file.c
--- a/head/contrib/libarchive/libarchive/archive_read_open_file.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_open_file.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_open_file.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_open_file.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -59,11 +59,7 @@
 
 static int	file_close(struct archive *, void *);
 static ssize_t	file_read(struct archive *, void *, const void **buff);
-#if ARCHIVE_API_VERSION < 2
-static ssize_t	file_skip(struct archive *, void *, size_t request);
-#else
-static off_t	file_skip(struct archive *, void *, off_t request);
-#endif
+static int64_t	file_skip(struct archive *, void *, int64_t request);
 
 int
 archive_read_open_FILE(struct archive *a, FILE *f)
@@ -101,8 +97,11 @@
 	setmode(fileno(mine->f), O_BINARY);
 #endif
 
-	return (archive_read_open2(a, mine, NULL, file_read,
-		    file_skip, file_close));
+	archive_read_set_read_callback(a, file_read);
+	archive_read_set_skip_callback(a, file_skip);
+	archive_read_set_close_callback(a, file_close);
+	archive_read_set_callback_data(a, mine);
+	return (archive_read_open1(a));
 }
 
 static ssize_t
@@ -119,15 +118,18 @@
 	return (bytes_read);
 }
 
-#if ARCHIVE_API_VERSION < 2
-static ssize_t
-file_skip(struct archive *a, void *client_data, size_t request)
-#else
-static off_t
-file_skip(struct archive *a, void *client_data, off_t request)
-#endif
+static int64_t
+file_skip(struct archive *a, void *client_data, int64_t request)
 {
 	struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
+#if HAVE_FSEEKO
+	off_t skip = (off_t)request;
+#elif HAVE__FSEEKI64
+	int64_t skip = request;
+#else
+	long skip = (long)request;
+#endif
+	int skip_bits = sizeof(skip) * 8 - 1;
 
 	(void)a; /* UNUSED */
 
@@ -140,10 +142,20 @@
 	if (request == 0)
 		return (0);
 
+	/* If request is too big for a long or an off_t, reduce it. */
+	if (sizeof(request) > sizeof(skip)) {
+		int64_t max_skip =
+		    (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;
+		if (request > max_skip)
+			skip = max_skip;
+	}
+
 #if HAVE_FSEEKO
-	if (fseeko(mine->f, request, SEEK_CUR) != 0)
+	if (fseeko(mine->f, skip, SEEK_CUR) != 0)
+#elif HAVE__FSEEKI64
+	if (_fseeki64(mine->f, skip, SEEK_CUR) != 0)
 #else
-	if (fseek(mine->f, request, SEEK_CUR) != 0)
+	if (fseek(mine->f, skip, SEEK_CUR) != 0)
 #endif
 	{
 		mine->can_skip = 0;
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_open_filename.c
--- a/head/contrib/libarchive/libarchive/archive_read_open_filename.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_open_filename.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2003-2010 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,8 +24,11 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_open_filename.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_open_filename.c 232153 2012-02-25 10:58:02Z mm $");
 
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
@@ -47,8 +50,17 @@
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <sys/disk.h>
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#elif defined(__DragonFly__)
+#include <sys/diskslice.h>
+#endif
 
 #include "archive.h"
+#include "archive_string.h"
 
 #ifndef O_BINARY
 #define O_BINARY 0
@@ -59,17 +71,21 @@
 	size_t	 block_size;
 	void	*buffer;
 	mode_t	 st_mode;  /* Mode bits for opened file. */
-	char	 can_skip; /* This file supports skipping. */
-	char	 filename[1]; /* Must be last! */
+	char	 use_lseek;
+	enum fnt_e { FNT_STDIN, FNT_MBS, FNT_WCS } filename_type;
+	union {
+		char	 m[1];/* MBS filename. */
+		wchar_t	 w[1];/* WCS filename. */
+	} filename; /* Must be last! */
 };
 
 static int	file_close(struct archive *, void *);
+static int	file_open_filename(struct archive *, enum fnt_e, const void *,
+		    size_t);
 static ssize_t	file_read(struct archive *, void *, const void **buff);
-#if ARCHIVE_API_VERSION < 2
-static ssize_t	file_skip(struct archive *, void *, size_t request);
-#else
-static off_t	file_skip(struct archive *, void *, off_t request);
-#endif
+static int64_t	file_seek(struct archive *, void *, int64_t request, int);
+static int64_t	file_skip(struct archive *, void *, int64_t request);
+static int64_t	file_skip_lseek(struct archive *, void *, int64_t request);
 
 int
 archive_read_open_file(struct archive *a, const char *filename,
@@ -82,14 +98,75 @@
 archive_read_open_filename(struct archive *a, const char *filename,
     size_t block_size)
 {
+	enum fnt_e filename_type;
+
+	if (filename == NULL || filename[0] == '\0') {
+		filename_type = FNT_STDIN;
+	} else
+		filename_type = FNT_MBS;
+	return (file_open_filename(a, filename_type, filename, block_size));
+}
+
+int
+archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename,
+    size_t block_size)
+{
+	enum fnt_e filename_type;
+
+	if (wfilename == NULL || wfilename[0] == L'\0') {
+		filename_type = FNT_STDIN;
+	} else {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		filename_type = FNT_WCS;
+#else
+		/*
+		 * POSIX system does not support a wchar_t interface for
+		 * open() system call, so we have to translate a whcar_t
+		 * filename to multi-byte one and use it.
+		 */
+		struct archive_string fn;
+		int r;
+
+		archive_string_init(&fn);
+		if (archive_string_append_from_wcs(&fn, wfilename,
+		    wcslen(wfilename)) != 0) {
+			archive_set_error(a, EINVAL,
+			    "Failed to convert a wide-character filename to"
+			    " a multi-byte filename");
+			archive_string_free(&fn);
+			return (ARCHIVE_FATAL);
+		}
+		r = file_open_filename(a, FNT_MBS, fn.s, block_size);
+		archive_string_free(&fn);
+		return (r);
+#endif
+	}
+	return (file_open_filename(a, filename_type, wfilename, block_size));
+}
+
+static int
+file_open_filename(struct archive *a, enum fnt_e filename_type,
+    const void *_filename, size_t block_size)
+{
 	struct stat st;
 	struct read_file_data *mine;
-	void *b;
+	void *buffer;
+	const char *filename = NULL;
+	const wchar_t *wfilename = NULL;
 	int fd;
+	int is_disk_like = 0;
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+	off_t mediasize = 0; /* FreeBSD-specific, so off_t okay here. */
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+	struct disklabel dl;
+#elif defined(__DragonFly__)
+	struct partinfo pi;
+#endif
 
 	archive_clear_error(a);
-	if (filename == NULL || filename[0] == '\0') {
-		/* We used to invoke archive_read_open_fd(a,0,block_size)
+	if (filename_type == FNT_STDIN) {
+		/* We used to delegate stdin support by
+		 * directly calling archive_read_open_fd(a,0,block_size)
 		 * here, but that doesn't (and shouldn't) handle the
 		 * end-of-file flush when reading stdout from a pipe.
 		 * Basically, read_open_fd() is intended for folks who
@@ -97,60 +174,152 @@
 		 * API is intended to be a little smarter for folks who
 		 * want easy handling of the common case.
 		 */
-		filename = ""; /* Normalize NULL to "" */
 		fd = 0;
 #if defined(__CYGWIN__) || defined(_WIN32)
 		setmode(0, O_BINARY);
 #endif
-	} else {
+		filename = "";
+	} else if (filename_type == FNT_MBS) {
+		filename = (const char *)_filename;
 		fd = open(filename, O_RDONLY | O_BINARY);
 		if (fd < 0) {
 			archive_set_error(a, errno,
 			    "Failed to open '%s'", filename);
 			return (ARCHIVE_FATAL);
 		}
+	} else {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		wfilename = (const wchar_t *)_filename;
+		fd = _wopen(wfilename, O_RDONLY | O_BINARY);
+		if (fd < 0 && errno == ENOENT) {
+			wchar_t *fullpath;
+			fullpath = __la_win_permissive_name_w(wfilename);
+			if (fullpath != NULL) {
+				fd = _wopen(fullpath, O_RDONLY | O_BINARY);
+				free(fullpath);
+			}
+		}
+		if (fd < 0) {
+			archive_set_error(a, errno,
+			    "Failed to open '%S'", wfilename);
+			return (ARCHIVE_FATAL);
+		}
+#else
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Unexpedted operation in archive_read_open_filename");
+		return (ARCHIVE_FATAL);
+#endif
 	}
 	if (fstat(fd, &st) != 0) {
-		archive_set_error(a, errno, "Can't stat '%s'", filename);
+		if (filename_type == FNT_WCS)
+			archive_set_error(a, errno, "Can't stat '%S'",
+			    wfilename);
+		else
+			archive_set_error(a, errno, "Can't stat '%s'",
+			    filename);
 		return (ARCHIVE_FATAL);
 	}
 
-	mine = (struct read_file_data *)calloc(1,
-	    sizeof(*mine) + strlen(filename));
-	b = malloc(block_size);
-	if (mine == NULL || b == NULL) {
+	/*
+	 * Determine whether the input looks like a disk device or a
+	 * tape device.  The results are used below to select an I/O
+	 * strategy:
+	 *  = "disk-like" devices support arbitrary lseek() and will
+	 *    support I/O requests of any size.  So we get easy skipping
+	 *    and can cheat on block sizes to get better performance.
+	 *  = "tape-like" devices require strict blocking and use
+	 *    specialized ioctls for seeking.
+	 *  = "socket-like" devices cannot seek at all but can improve
+	 *    performance by using nonblocking I/O to read "whatever is
+	 *    available right now".
+	 *
+	 * Right now, we only specially recognize disk-like devices,
+	 * but it should be straightforward to add probes and strategy
+	 * here for tape-like and socket-like devices.
+	 */
+	if (S_ISREG(st.st_mode)) {
+		/* Safety:  Tell the extractor not to overwrite the input. */
+		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+		/* Regular files act like disks. */
+		is_disk_like = 1;
+	}
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+	/* FreeBSD: if it supports DIOCGMEDIASIZE ioctl, it's disk-like. */
+	else if (S_ISCHR(st.st_mode) &&
+	    ioctl(fd, DIOCGMEDIASIZE, &mediasize) == 0 &&
+	    mediasize > 0) {
+		is_disk_like = 1;
+	}
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+	/* Net/OpenBSD: if it supports DIOCGDINFO ioctl, it's disk-like. */
+	else if ((S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) &&
+	    ioctl(fd, DIOCGDINFO, &dl) == 0 &&
+	    dl.d_partitions[DISKPART(st.st_rdev)].p_size > 0) {
+		is_disk_like = 1;
+	}
+#elif defined(__DragonFly__)
+	/* DragonFly BSD:  if it supports DIOCGPART ioctl, it's disk-like. */
+	else if (S_ISCHR(st.st_mode) &&
+	    ioctl(fd, DIOCGPART, &pi) == 0 &&
+	    pi.media_size > 0) {
+		is_disk_like = 1;
+	}
+#elif defined(__linux__)
+	/* Linux:  All block devices are disk-like. */
+	else if (S_ISBLK(st.st_mode) &&
+	    lseek(fd, 0, SEEK_CUR) == 0 &&
+	    lseek(fd, 0, SEEK_SET) == 0 &&
+	    lseek(fd, 0, SEEK_END) > 0 &&
+	    lseek(fd, 0, SEEK_SET) == 0) {
+		is_disk_like = 1;
+	}
+#endif
+	/* TODO: Add an "is_tape_like" variable and appropriate tests. */
+
+	if (filename_type == FNT_WCS)
+		mine = (struct read_file_data *)calloc(1,
+		    sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t));
+	else
+		mine = (struct read_file_data *)calloc(1,
+		    sizeof(*mine) + strlen(filename));
+	/* Disk-like devices prefer power-of-two block sizes.  */
+	/* Use provided block_size as a guide so users have some control. */
+	if (is_disk_like) {
+		size_t new_block_size = 64 * 1024;
+		while (new_block_size < block_size
+		    && new_block_size < 64 * 1024 * 1024)
+			new_block_size *= 2;
+		block_size = new_block_size;
+	}
+	buffer = malloc(block_size);
+	if (mine == NULL || buffer == NULL) {
 		archive_set_error(a, ENOMEM, "No memory");
 		free(mine);
-		free(b);
+		free(buffer);
 		return (ARCHIVE_FATAL);
 	}
-	strcpy(mine->filename, filename);
+	if (filename_type == FNT_WCS)
+		wcscpy(mine->filename.w, wfilename);
+	else
+		strcpy(mine->filename.m, filename);
+	mine->filename_type = filename_type;
 	mine->block_size = block_size;
-	mine->buffer = b;
+	mine->buffer = buffer;
 	mine->fd = fd;
 	/* Remember mode so close can decide whether to flush. */
 	mine->st_mode = st.st_mode;
-	/* If we're reading a file from disk, ensure that we don't
-	   overwrite it with an extracted file. */
-	if (S_ISREG(st.st_mode)) {
-		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
-		/*
-		 * Enabling skip here is a performance optimization
-		 * for anything that supports lseek().  On FreeBSD
-		 * (and probably many other systems), only regular
-		 * files and raw disk devices support lseek() (on
-		 * other input types, lseek() returns success but
-		 * doesn't actually change the file pointer, which
-		 * just completely screws up the position-tracking
-		 * logic).  In addition, I've yet to find a portable
-		 * way to determine if a device is a raw disk device.
-		 * So I don't see a way to do much better than to only
-		 * enable this optimization for regular files.
-		 */
-		mine->can_skip = 1;
+
+	/* Disk-like inputs can use lseek(). */
+	if (is_disk_like) {
+		archive_read_set_seek_callback(a, file_seek);
+		mine->use_lseek = 1;
 	}
-	return (archive_read_open2(a, mine,
-		NULL, file_read, file_skip, file_close));
+
+	archive_read_set_read_callback(a, file_read);
+	archive_read_set_skip_callback(a, file_skip);
+	archive_read_set_close_callback(a, file_close);
+	archive_read_set_callback_data(a, mine);
+	return (archive_read_open1(a));
 }
 
 static ssize_t
@@ -159,79 +328,146 @@
 	struct read_file_data *mine = (struct read_file_data *)client_data;
 	ssize_t bytes_read;
 
+	/* TODO: If a recent lseek() operation has left us
+	 * mis-aligned, read and return a short block to try to get
+	 * us back in alignment. */
+
+	/* TODO: Someday, try mmap() here; if that succeeds, give
+	 * the entire file to libarchive as a single block.  That
+	 * could be a lot faster than block-by-block manual I/O. */
+
+	/* TODO: We might be able to improve performance on pipes and
+	 * sockets by setting non-blocking I/O and just accepting
+	 * whatever we get here instead of waiting for a full block
+	 * worth of data. */
+
 	*buff = mine->buffer;
 	for (;;) {
 		bytes_read = read(mine->fd, mine->buffer, mine->block_size);
 		if (bytes_read < 0) {
 			if (errno == EINTR)
 				continue;
-			else if (mine->filename[0] == '\0')
-				archive_set_error(a, errno, "Error reading stdin");
+			else if (mine->filename_type == FNT_STDIN)
+				archive_set_error(a, errno,
+				    "Error reading stdin");
+			else if (mine->filename_type == FNT_MBS)
+				archive_set_error(a, errno,
+				    "Error reading '%s'", mine->filename.m);
 			else
-				archive_set_error(a, errno, "Error reading '%s'",
-				    mine->filename);
+				archive_set_error(a, errno,
+				    "Error reading '%S'", mine->filename.w);
 		}
 		return (bytes_read);
 	}
 }
 
-#if ARCHIVE_API_VERSION < 2
-static ssize_t
-file_skip(struct archive *a, void *client_data, size_t request)
-#else
-static off_t
-file_skip(struct archive *a, void *client_data, off_t request)
-#endif
+/*
+ * Regular files and disk-like block devices can use simple lseek
+ * without needing to round the request to the block size.
+ *
+ * TODO: This can leave future reads mis-aligned.  Since we know the
+ * offset here, we should store it and use it in file_read() above
+ * to determine whether we should perform a short read to get back
+ * into alignment.  Long series of mis-aligned reads can negatively
+ * impact disk throughput.  (Of course, the performance impact should
+ * be carefully tested; extra code complexity is only worthwhile if
+ * it does provide measurable improvement.)
+ *
+ * TODO: Be lazy about the actual seek.  There are a few pathological
+ * cases where libarchive makes a bunch of seek requests in a row
+ * without any intervening reads.  This isn't a huge performance
+ * problem, since the kernel handles seeks lazily already, but
+ * it would be very slightly faster if we simply remembered the
+ * seek request here and then actually performed the seek at the
+ * top of the read callback above.
+ */
+static int64_t
+file_skip_lseek(struct archive *a, void *client_data, int64_t request)
 {
 	struct read_file_data *mine = (struct read_file_data *)client_data;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* We use _lseeki64() on Windows. */
+	int64_t old_offset, new_offset;
+#else
 	off_t old_offset, new_offset;
+#endif
 
-	if (!mine->can_skip) /* We can't skip, so ... */
-		return (0); /* ... skip zero bytes. */
+	/* We use off_t here because lseek() is declared that way. */
 
-	/* Reduce request to the next smallest multiple of block_size */
-	request = (request / mine->block_size) * mine->block_size;
-	if (request == 0)
+	/* TODO: Deal with case where off_t isn't 64 bits.
+	 * This shouldn't be a problem on Linux or other POSIX
+	 * systems, since the configuration logic for libarchive
+	 * tries to obtain a 64-bit off_t.  It's still an issue
+	 * on Windows, though, so it might suffice to just use
+	 * _lseeki64() on Windows.
+	 */
+	if ((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0 &&
+	    (new_offset = lseek(mine->fd, request, SEEK_CUR)) >= 0)
+		return (new_offset - old_offset);
+
+	/* If lseek() fails, don't bother trying again. */
+	mine->use_lseek = 0;
+
+	/* Let libarchive recover with read+discard */
+	if (errno == ESPIPE)
 		return (0);
 
-	/*
-	 * Hurray for lazy evaluation: if the first lseek fails, the second
-	 * one will not be executed.
-	 */
-	if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) ||
-	    ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0))
-	{
-		/* If skip failed once, it will probably fail again. */
-		mine->can_skip = 0;
+	/* If the input is corrupted or truncated, fail. */
+	if (mine->filename_type == FNT_STDIN)
+		archive_set_error(a, errno, "Error seeking in stdin");
+	else if (mine->filename_type == FNT_MBS)
+		archive_set_error(a, errno, "Error seeking in '%s'",
+		    mine->filename.m);
+	else
+		archive_set_error(a, errno, "Error seeking in '%S'",
+		    mine->filename.w);
+	return (-1);
+}
 
-		if (errno == ESPIPE)
-		{
-			/*
-			 * Failure to lseek() can be caused by the file
-			 * descriptor pointing to a pipe, socket or FIFO.
-			 * Return 0 here, so the compression layer will use
-			 * read()s instead to advance the file descriptor.
-			 * It's slower of course, but works as well.
-			 */
-			return (0);
-		}
-		/*
-		 * There's been an error other than ESPIPE. This is most
-		 * likely caused by a programmer error (too large request)
-		 * or a corrupted archive file.
-		 */
-		if (mine->filename[0] == '\0')
-			/*
-			 * Should never get here, since lseek() on stdin ought
-			 * to return an ESPIPE error.
-			 */
-			archive_set_error(a, errno, "Error seeking in stdin");
-		else
-			archive_set_error(a, errno, "Error seeking in '%s'",
-			    mine->filename);
-		return (-1);
-	}
-	return (new_offset - old_offset);
+
+/*
+ * TODO: Implement another file_skip_XXXX that uses MTIO ioctls to
+ * accelerate operation on tape drives.
+ */
+
+static int64_t
+file_skip(struct archive *a, void *client_data, int64_t request)
+{
+	struct read_file_data *mine = (struct read_file_data *)client_data;
+
+	/* Delegate skip requests. */
+	if (mine->use_lseek)
+		return (file_skip_lseek(a, client_data, request));
+
+	/* If we can't skip, return 0; libarchive will read+discard instead. */
+	return (0);
+}
+
+/*
+ * TODO: Store the offset and use it in the read callback.
+ */
+static int64_t
+file_seek(struct archive *a, void *client_data, int64_t request, int whence)
+{
+	struct read_file_data *mine = (struct read_file_data *)client_data;
+	off_t r;
+
+	/* We use off_t here because lseek() is declared that way. */
+	/* See above for notes about when off_t is less than 64 bits. */
+	r = lseek(mine->fd, request, whence);
+	if (r >= 0)
+		return r;
+
+	/* If the input is corrupted or truncated, fail. */
+	if (mine->filename_type == FNT_STDIN)
+		archive_set_error(a, errno, "Error seeking in stdin");
+	else if (mine->filename_type == FNT_MBS)
+		archive_set_error(a, errno, "Error seeking in '%s'",
+		    mine->filename.m);
+	else
+		archive_set_error(a, errno, "Error seeking in '%S'",
+		    mine->filename.w);
+	return (ARCHIVE_FATAL);
 }
 
 static int
@@ -246,7 +482,8 @@
 		/*
 		 * Sometimes, we should flush the input before closing.
 		 *   Regular files: faster to just close without flush.
-		 *   Devices: must not flush (user might need to
+		 *   Disk-like devices:  Ditto.
+		 *   Tapes: must not flush (user might need to
 		 *      read the "next" item on a non-rewind device).
 		 *   Pipes and sockets:  must flush (otherwise, the
 		 *      program feeding the pipe or socket may complain).
@@ -263,7 +500,7 @@
 			} while (bytesRead > 0);
 		}
 		/* If a named file was opened, then it needs to be closed. */
-		if (mine->filename[0] != '\0')
+		if (mine->filename_type != FNT_STDIN)
 			close(mine->fd);
 	}
 	free(mine->buffer);
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_open_memory.c
--- a/head/contrib/libarchive/libarchive/archive_read_open_memory.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_open_memory.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_open_memory.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_open_memory.c 232153 2012-02-25 10:58:02Z mm $");
 
 #include <errno.h>
 #include <stdlib.h>
@@ -41,18 +41,16 @@
  */
 
 struct read_memory_data {
-	unsigned char	*buffer;
+	unsigned char	*start;
+	unsigned char	*p;
 	unsigned char	*end;
 	ssize_t	 read_size;
 };
 
 static int	memory_read_close(struct archive *, void *);
 static int	memory_read_open(struct archive *, void *);
-#if ARCHIVE_API_VERSION < 2
-static ssize_t	memory_read_skip(struct archive *, void *, size_t request);
-#else
-static off_t	memory_read_skip(struct archive *, void *, off_t request);
-#endif
+static int64_t	memory_read_seek(struct archive *, void *, int64_t offset, int whence);
+static int64_t	memory_read_skip(struct archive *, void *, int64_t request);
 static ssize_t	memory_read(struct archive *, void *, const void **buff);
 
 int
@@ -78,11 +76,16 @@
 		return (ARCHIVE_FATAL);
 	}
 	memset(mine, 0, sizeof(*mine));
-	mine->buffer = (unsigned char *)buff;
-	mine->end = mine->buffer + size;
+	mine->start = mine->p = (unsigned char *)buff;
+	mine->end = mine->start + size;
 	mine->read_size = read_size;
-	return (archive_read_open2(a, mine, memory_read_open,
-		    memory_read, memory_read_skip, memory_read_close));
+	archive_read_set_open_callback(a, memory_read_open);
+	archive_read_set_read_callback(a, memory_read);
+	archive_read_set_seek_callback(a, memory_read_seek);
+	archive_read_set_skip_callback(a, memory_read_skip);
+	archive_read_set_close_callback(a, memory_read_close);
+	archive_read_set_callback_data(a, mine);
+	return (archive_read_open1(a));
 }
 
 /*
@@ -110,11 +113,11 @@
 	ssize_t size;
 
 	(void)a; /* UNUSED */
-	*buff = mine->buffer;
-	size = mine->end - mine->buffer;
+	*buff = mine->p;
+	size = mine->end - mine->p;
 	if (size > mine->read_size)
 		size = mine->read_size;
-        mine->buffer += size;
+        mine->p += size;
 	return (size);
 }
 
@@ -123,27 +126,55 @@
  * necessary in order to better exercise internal code when used
  * as a test harness.
  */
-#if ARCHIVE_API_VERSION < 2
-static ssize_t
-memory_read_skip(struct archive *a, void *client_data, size_t skip)
-#else
-static off_t
-memory_read_skip(struct archive *a, void *client_data, off_t skip)
-#endif
+static int64_t
+memory_read_skip(struct archive *a, void *client_data, int64_t skip)
 {
 	struct read_memory_data *mine = (struct read_memory_data *)client_data;
 
 	(void)a; /* UNUSED */
-	if ((off_t)skip > (off_t)(mine->end - mine->buffer))
-		skip = mine->end - mine->buffer;
+	if ((int64_t)skip > (int64_t)(mine->end - mine->p))
+		skip = mine->end - mine->p;
 	/* Round down to block size. */
 	skip /= mine->read_size;
 	skip *= mine->read_size;
-	mine->buffer += skip;
+	mine->p += skip;
 	return (skip);
 }
 
 /*
+ * Seeking.
+ */
+static int64_t
+memory_read_seek(struct archive *a, void *client_data, int64_t offset, int whence)
+{
+	struct read_memory_data *mine = (struct read_memory_data *)client_data;
+
+	(void)a; /* UNUSED */
+	switch (whence) {
+	case SEEK_SET:
+		mine->p = mine->start + offset;
+		break;
+	case SEEK_CUR:
+		mine->p += offset;
+		break;
+	case SEEK_END:
+		mine->p = mine->end + offset;
+		break;
+	default:
+		return ARCHIVE_FATAL;
+	}
+	if (mine->p < mine->start) {
+		mine->p = mine->start;
+		return ARCHIVE_FAILED;
+	}
+	if (mine->p > mine->end) {
+		mine->p = mine->end;
+		return ARCHIVE_FAILED;
+	}
+	return (mine->p - mine->start);
+}
+
+/*
  * Close is just cleaning up our one small bit of data.
  */
 static int
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_private.h
--- a/head/contrib/libarchive/libarchive/archive_read_private.h	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_private.h	Fri Mar 02 16:54:40 2012 +0200
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/contrib/libarchive/libarchive/archive_read_private.h 228763 2011-12-21 11:13:29Z mm $
+ * $FreeBSD: head/contrib/libarchive/libarchive/archive_read_private.h 232153 2012-02-25 10:58:02Z mm $
  */
 
 #ifndef __LIBARCHIVE_BUILD
@@ -42,13 +42,18 @@
 
 /*
  * How bidding works for filters:
- *   * The bid manager reads the first block from the current source.
- *   * It shows that block to each registered bidder.
+ *   * The bid manager initializes the client-provided reader as the
+ *     first filter.
+ *   * It invokes the bidder for each registered filter with the
+ *     current head filter.
+ *   * The bidders can use archive_read_filter_ahead() to peek ahead
+ *     at the incoming data to compose their bids.
  *   * The bid manager creates a new filter structure for the winning
  *     bidder and gives the winning bidder a chance to initialize it.
- *   * The new filter becomes the top filter in the archive_read structure
- *     and we repeat the process.
- * This ends only when no bidder provides a non-zero bid.
+ *   * The new filter becomes the new top filter and we repeat the
+ *     process.
+ * This ends only when no bidder provides a non-zero bid.  Then
+ * we perform a similar dance with the registered format handlers.
  */
 struct archive_read_filter_bidder {
 	/* Configuration data for the bidder. */
@@ -71,6 +76,7 @@
  * corresponding bidder above.
  */
 struct archive_read_filter {
+	int64_t position;
 	/* Essentially all filters will need these values, so
 	 * just declare them here. */
 	struct archive_read_filter_bidder *bidder; /* My bidder. */
@@ -80,6 +86,8 @@
 	ssize_t (*read)(struct archive_read_filter *, const void **);
 	/* Skip forward this many bytes. */
 	int64_t (*skip)(struct archive_read_filter *self, int64_t request);
+	/* Seek to an absolute location. */
+	int64_t (*seek)(struct archive_read_filter *self, int64_t offset, int whence);
 	/* Close (just this filter) and free(self). */
 	int (*close)(struct archive_read_filter *self);
 	/* My private data. */
@@ -97,8 +105,8 @@
 	size_t		 client_total;
 	const char	*client_next;
 	size_t		 client_avail;
-	int64_t		 position;
 	char		 end_of_file;
+	char		 closed;
 	char		 fatal;
 };
 
@@ -111,9 +119,12 @@
  * so should be deferred at least until libarchive 3.0.
  */
 struct archive_read_client {
+	archive_open_callback	*opener;
 	archive_read_callback	*reader;
 	archive_skip_callback	*skipper;
+	archive_seek_callback	*seeker;
 	archive_close_callback	*closer;
+	void *data;
 };
 
 struct archive_read {
@@ -122,6 +133,7 @@
 	struct archive_entry	*entry;
 
 	/* Dev/ino of the archive being read/written. */
+	int		  skip_file_set;
 	dev_t		  skip_file_dev;
 	ino_t		  skip_file_ino;
 
@@ -130,21 +142,21 @@
 	 * data to client buffers, filling gaps with zero bytes.
 	 */
 	const char	 *read_data_block;
-	off_t		  read_data_offset;
-	off_t		  read_data_output_offset;
+	int64_t		  read_data_offset;
+	int64_t		  read_data_output_offset;
 	size_t		  read_data_remaining;
 
 	/* Callbacks to open/read/write/close client archive stream. */
 	struct archive_read_client client;
 
 	/* Registered filter bidders. */
-	struct archive_read_filter_bidder bidders[8];
+	struct archive_read_filter_bidder bidders[9];
 
 	/* Last filter in chain */
 	struct archive_read_filter *filter;
 
 	/* File offset of beginning of most recently-read header. */
-	off_t		  header_position;
+	int64_t		  header_position;
 
 	/*
 	 * Format detection is mostly the same as compression
@@ -157,14 +169,14 @@
 	struct archive_format_descriptor {
 		void	 *data;
 		const char *name;
-		int	(*bid)(struct archive_read *);
+		int	(*bid)(struct archive_read *, int best_bid);
 		int	(*options)(struct archive_read *, const char *key,
 		    const char *value);
 		int	(*read_header)(struct archive_read *, struct archive_entry *);
-		int	(*read_data)(struct archive_read *, const void **, size_t *, off_t *);
+		int	(*read_data)(struct archive_read *, const void **, size_t *, int64_t *);
 		int	(*read_data_skip)(struct archive_read *);
 		int	(*cleanup)(struct archive_read *);
-	}	formats[9];
+	}	formats[16];
 	struct archive_format_descriptor	*format; /* Active format. */
 
 	/*
@@ -177,23 +189,22 @@
 int	__archive_read_register_format(struct archive_read *a,
 	    void *format_data,
 	    const char *name,
-	    int (*bid)(struct archive_read *),
+	    int (*bid)(struct archive_read *, int),
 	    int (*options)(struct archive_read *, const char *, const char *),
 	    int (*read_header)(struct archive_read *, struct archive_entry *),
-	    int (*read_data)(struct archive_read *, const void **, size_t *, off_t *),
+	    int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *),
 	    int (*read_data_skip)(struct archive_read *),
 	    int (*cleanup)(struct archive_read *));
 
-struct archive_read_filter_bidder
-	*__archive_read_get_bidder(struct archive_read *a);
+int __archive_read_get_bidder(struct archive_read *a,
+    struct archive_read_filter_bidder **bidder);
 
 const void *__archive_read_ahead(struct archive_read *, size_t, ssize_t *);
 const void *__archive_read_filter_ahead(struct archive_read_filter *,
     size_t, ssize_t *);
-ssize_t	__archive_read_consume(struct archive_read *, size_t);
-ssize_t	__archive_read_filter_consume(struct archive_read_filter *, size_t);
-int64_t	__archive_read_skip(struct archive_read *, int64_t);
-int64_t	__archive_read_skip_lenient(struct archive_read *, int64_t);
-int64_t	__archive_read_filter_skip(struct archive_read_filter *, int64_t);
+int64_t	__archive_read_seek(struct archive_read*, int64_t, int);
+int64_t	__archive_read_filter_seek(struct archive_read_filter *, int64_t, int);
+int64_t	__archive_read_consume(struct archive_read *, int64_t);
+int64_t	__archive_read_filter_consume(struct archive_read_filter *, int64_t);
 int __archive_read_program(struct archive_read_filter *, const char *);
 #endif
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_support_format_all.c
--- a/head/contrib/libarchive/libarchive/archive_read_support_format_all.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_support_format_all.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,20 +24,56 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_all.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_all.c 232153 2012-02-25 10:58:02Z mm $");
 
 #include "archive.h"
+#include "archive_private.h"
 
 int
 archive_read_support_format_all(struct archive *a)
 {
+	archive_check_magic(a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_all");
+
+	/* TODO: It would be nice to compute the ordering
+	 * here automatically so that people who enable just
+	 * a few formats can still get the benefits.  That
+	 * may just require the format registration to include
+	 * a "maximum read-ahead" value (anything that uses seek
+	 * would be essentially infinite read-ahead).  The core
+	 * bid management can then sort the bidders before calling
+	 * them.
+	 *
+	 * If you implement the above, please return the list below
+	 * to alphabetic order.
+	 */
+
+	/*
+	 * These bidders are all pretty cheap; they just examine a
+	 * small initial part of the archive.  If one of these bids
+	 * high, we can maybe avoid running any of the more expensive
+	 * bidders below.
+	 */
 	archive_read_support_format_ar(a);
 	archive_read_support_format_cpio(a);
 	archive_read_support_format_empty(a);
-	archive_read_support_format_iso9660(a);
+	archive_read_support_format_lha(a);
 	archive_read_support_format_mtree(a);
 	archive_read_support_format_tar(a);
 	archive_read_support_format_xar(a);
+
+	/*
+	 * Install expensive bidders last.  By doing them last, we
+	 * increase the chance that a high bid from someone else will
+	 * make it unnecessary for these to do anything at all.
+	 */
+	/* These three have potentially large look-ahead. */
+	archive_read_support_format_7zip(a);
+	archive_read_support_format_cab(a);
+	archive_read_support_format_rar(a);
+	archive_read_support_format_iso9660(a);
+	/* Seek is really bad, since it forces the read-ahead
+	 * logic to discard buffered data. */
 	archive_read_support_format_zip(a);
 
 	/* Note: We always return ARCHIVE_OK here, even if some of the
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_support_format_ar.c
--- a/head/contrib/libarchive/libarchive/archive_read_support_format_ar.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_support_format_ar.c	Fri Mar 02 16:54:40 2012 +0200
@@ -26,7 +26,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_ar.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_ar.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -50,11 +50,17 @@
 #include "archive_read_private.h"
 
 struct ar {
-	off_t	 entry_bytes_remaining;
-	off_t	 entry_offset;
-	off_t	 entry_padding;
+	int64_t	 entry_bytes_remaining;
+	/* unconsumed is purely to track data we've gotten from readahead,
+	 * but haven't yet marked as consumed.  Must be paired with
+	 * entry_bytes_remaining usage/modification.
+	 */
+	size_t   entry_bytes_unconsumed;
+	int64_t	 entry_offset;
+	int64_t	 entry_padding;
 	char	*strtab;
 	size_t	 strtab_size;
+	char	 read_global_header;
 };
 
 /*
@@ -75,10 +81,10 @@
 #define AR_fmag_offset 58
 #define AR_fmag_size 2
 
-static int	archive_read_format_ar_bid(struct archive_read *a);
+static int	archive_read_format_ar_bid(struct archive_read *a, int);
 static int	archive_read_format_ar_cleanup(struct archive_read *a);
 static int	archive_read_format_ar_read_data(struct archive_read *a,
-		    const void **buff, size_t *size, off_t *offset);
+		    const void **buff, size_t *size, int64_t *offset);
 static int	archive_read_format_ar_skip(struct archive_read *a);
 static int	archive_read_format_ar_read_header(struct archive_read *a,
 		    struct archive_entry *e);
@@ -95,6 +101,9 @@
 	struct ar *ar;
 	int r;
 
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_ar");
+
 	ar = (struct ar *)malloc(sizeof(*ar));
 	if (ar == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
@@ -135,14 +144,11 @@
 }
 
 static int
-archive_read_format_ar_bid(struct archive_read *a)
+archive_read_format_ar_bid(struct archive_read *a, int best_bid)
 {
 	const void *h;
 
-	if (a->archive.archive_format != 0 &&
-	    (a->archive.archive_format & ARCHIVE_FORMAT_BASE_MASK) !=
-	    ARCHIVE_FORMAT_AR)
-		return(0);
+	(void)best_bid; /* UNUSED */
 
 	/*
 	 * Verify the 8-byte file signature.
@@ -150,45 +156,23 @@
 	 */
 	if ((h = __archive_read_ahead(a, 8, NULL)) == NULL)
 		return (-1);
-	if (strncmp((const char*)h, "!<arch>\n", 8) == 0) {
+	if (memcmp(h, "!<arch>\n", 8) == 0) {
 		return (64);
 	}
 	return (-1);
 }
 
 static int
-archive_read_format_ar_read_header(struct archive_read *a,
-    struct archive_entry *entry)
+_ar_read_header(struct archive_read *a, struct archive_entry *entry,
+	struct ar *ar, const char *h, size_t *unconsumed)
 {
 	char filename[AR_name_size + 1];
-	struct ar *ar;
 	uint64_t number; /* Used to hold parsed numbers before validation. */
-	ssize_t bytes_read;
 	size_t bsd_name_length, entry_size;
 	char *p, *st;
 	const void *b;
-	const char *h;
 	int r;
 
-	ar = (struct ar*)(a->format->data);
-
-	if (a->archive.file_position == 0) {
-		/*
-		 * We are now at the beginning of the archive,
-		 * so we need first consume the ar global header.
-		 */
-		__archive_read_consume(a, 8);
-		/* Set a default format code for now. */
-		a->archive.archive_format = ARCHIVE_FORMAT_AR;
-	}
-
-	/* Read the header for the next file entry. */
-	if ((b = __archive_read_ahead(a, 60, &bytes_read)) == NULL)
-		/* Broken header. */
-		return (ARCHIVE_EOF);
-	__archive_read_consume(a, 60);
-	h = (const char *)b;
-
 	/* Verify the magic signature on the file header. */
 	if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) {
 		archive_set_error(&a->archive, EINVAL,
@@ -292,6 +276,12 @@
 		}
 		ar->strtab = st;
 		ar->strtab_size = entry_size;
+
+		if (*unconsumed) {
+			__archive_read_consume(a, *unconsumed);
+			*unconsumed = 0;
+		}
+
 		if ((b = __archive_read_ahead(a, entry_size, NULL)) == NULL)
 			return (ARCHIVE_FATAL);
 		memcpy(st, b, entry_size);
@@ -347,7 +337,7 @@
 		 * overflowing a size_t and against the filename size
 		 * being larger than the entire entry. */
 		if (number > (uint64_t)(bsd_name_length + 1)
-		    || (off_t)bsd_name_length > ar->entry_bytes_remaining) {
+		    || (int64_t)bsd_name_length > ar->entry_bytes_remaining) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Bad input file size");
 			return (ARCHIVE_FATAL);
@@ -356,14 +346,17 @@
 		/* Adjust file size reported to client. */
 		archive_entry_set_size(entry, ar->entry_bytes_remaining);
 
+		if (*unconsumed) {
+			__archive_read_consume(a, *unconsumed);
+			*unconsumed = 0;
+		}
+
 		/* Read the long name into memory. */
 		if ((b = __archive_read_ahead(a, bsd_name_length, NULL)) == NULL) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "Truncated input file");
 			return (ARCHIVE_FATAL);
 		}
-		__archive_read_consume(a, bsd_name_length);
-
 		/* Store it in the entry. */
 		p = (char *)malloc(bsd_name_length + 1);
 		if (p == NULL) {
@@ -373,6 +366,9 @@
 		}
 		strncpy(p, b, bsd_name_length);
 		p[bsd_name_length] = '\0';
+
+		__archive_read_consume(a, bsd_name_length);
+
 		archive_entry_copy_pathname(entry, p);
 		free(p);
 		return (ARCHIVE_OK);
@@ -409,6 +405,42 @@
 }
 
 static int
+archive_read_format_ar_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	struct ar *ar = (struct ar*)(a->format->data);
+	size_t unconsumed;
+	const void *header_data;
+	int ret;
+
+	if (!ar->read_global_header) {
+		/*
+		 * We are now at the beginning of the archive,
+		 * so we need first consume the ar global header.
+		 */
+		__archive_read_consume(a, 8);
+		ar->read_global_header = 1;
+		/* Set a default format code for now. */
+		a->archive.archive_format = ARCHIVE_FORMAT_AR;
+	}
+
+	/* Read the header for the next file entry. */
+	if ((header_data = __archive_read_ahead(a, 60, NULL)) == NULL)
+		/* Broken header. */
+		return (ARCHIVE_EOF);
+	
+	unconsumed = 60;
+	
+	ret = _ar_read_header(a, entry, ar, (const char *)header_data, &unconsumed);
+
+	if (unconsumed)
+		__archive_read_consume(a, unconsumed);
+
+	return ret;
+}
+
+
+static int
 ar_parse_common_header(struct ar *ar, struct archive_entry *entry,
     const char *h)
 {
@@ -434,13 +466,18 @@
 
 static int
 archive_read_format_ar_read_data(struct archive_read *a,
-    const void **buff, size_t *size, off_t *offset)
+    const void **buff, size_t *size, int64_t *offset)
 {
 	ssize_t bytes_read;
 	struct ar *ar;
 
 	ar = (struct ar *)(a->format->data);
 
+	if (ar->entry_bytes_unconsumed) {
+		__archive_read_consume(a, ar->entry_bytes_unconsumed);
+		ar->entry_bytes_unconsumed = 0;
+	}
+
 	if (ar->entry_bytes_remaining > 0) {
 		*buff = __archive_read_ahead(a, 1, &bytes_read);
 		if (bytes_read == 0) {
@@ -453,20 +490,22 @@
 		if (bytes_read > ar->entry_bytes_remaining)
 			bytes_read = (ssize_t)ar->entry_bytes_remaining;
 		*size = bytes_read;
+		ar->entry_bytes_unconsumed = bytes_read;
 		*offset = ar->entry_offset;
 		ar->entry_offset += bytes_read;
 		ar->entry_bytes_remaining -= bytes_read;
-		__archive_read_consume(a, (size_t)bytes_read);
 		return (ARCHIVE_OK);
 	} else {
-		while (ar->entry_padding > 0) {
-			*buff = __archive_read_ahead(a, 1, &bytes_read);
-			if (bytes_read <= 0)
-				return (ARCHIVE_FATAL);
-			if (bytes_read > ar->entry_padding)
-				bytes_read = (ssize_t)ar->entry_padding;
-			__archive_read_consume(a, (size_t)bytes_read);
-			ar->entry_padding -= bytes_read;
+		int64_t skipped = __archive_read_consume(a, ar->entry_padding);
+		if (skipped >= 0) {
+			ar->entry_padding -= skipped;
+		}
+		if (ar->entry_padding) {
+			if (skipped >= 0) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+					"Truncated ar archive- failed consuming padding");
+			}
+			return (ARCHIVE_FATAL);
 		}
 		*buff = NULL;
 		*size = 0;
@@ -478,17 +517,19 @@
 static int
 archive_read_format_ar_skip(struct archive_read *a)
 {
-	off_t bytes_skipped;
+	int64_t bytes_skipped;
 	struct ar* ar;
 
 	ar = (struct ar *)(a->format->data);
 
-	bytes_skipped = __archive_read_skip(a,
-	    ar->entry_bytes_remaining + ar->entry_padding);
+	bytes_skipped = __archive_read_consume(a,
+	    ar->entry_bytes_remaining + ar->entry_padding
+	    + ar->entry_bytes_unconsumed);
 	if (bytes_skipped < 0)
 		return (ARCHIVE_FATAL);
 
 	ar->entry_bytes_remaining = 0;
+	ar->entry_bytes_unconsumed = 0;
 	ar->entry_padding = 0;
 
 	return (ARCHIVE_OK);
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_support_format_cpio.c
--- a/head/contrib/libarchive/libarchive/archive_read_support_format_cpio.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_support_format_cpio.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,7 +25,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_cpio.c 228911 2011-12-27 10:36:56Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_cpio.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -39,62 +40,127 @@
 
 #include "archive.h"
 #include "archive_entry.h"
+#include "archive_entry_locale.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
 
-#ifdef _MSC_VER
-#define __packed
-#pragma pack(push, 1)
-#endif
-struct cpio_bin_header {
-	unsigned char	c_magic[2];
-	unsigned char	c_dev[2];
-	unsigned char	c_ino[2];
-	unsigned char	c_mode[2];
-	unsigned char	c_uid[2];
-	unsigned char	c_gid[2];
-	unsigned char	c_nlink[2];
-	unsigned char	c_rdev[2];
-	unsigned char	c_mtime[4];
-	unsigned char	c_namesize[2];
-	unsigned char	c_filesize[4];
-} __packed;
+#define	bin_magic_offset 0
+#define	bin_magic_size 2
+#define	bin_dev_offset 2
+#define	bin_dev_size 2
+#define	bin_ino_offset 4
+#define	bin_ino_size 2
+#define	bin_mode_offset 6
+#define	bin_mode_size 2
+#define	bin_uid_offset 8
+#define	bin_uid_size 2
+#define	bin_gid_offset 10
+#define	bin_gid_size 2
+#define	bin_nlink_offset 12
+#define	bin_nlink_size 2
+#define	bin_rdev_offset 14
+#define	bin_rdev_size 2
+#define	bin_mtime_offset 16
+#define	bin_mtime_size 4
+#define	bin_namesize_offset 20
+#define	bin_namesize_size 2
+#define	bin_filesize_offset 22
+#define	bin_filesize_size 4
+#define	bin_header_size 26
 
-struct cpio_odc_header {
-	char	c_magic[6];
-	char	c_dev[6];
-	char	c_ino[6];
-	char	c_mode[6];
-	char	c_uid[6];
-	char	c_gid[6];
-	char	c_nlink[6];
-	char	c_rdev[6];
-	char	c_mtime[11];
-	char	c_namesize[6];
-	char	c_filesize[11];
-} __packed;
+#define	odc_magic_offset 0
+#define	odc_magic_size 6
+#define	odc_dev_offset 6
+#define	odc_dev_size 6
+#define	odc_ino_offset 12
+#define	odc_ino_size 6
+#define	odc_mode_offset 18
+#define	odc_mode_size 6
+#define	odc_uid_offset 24
+#define	odc_uid_size 6
+#define	odc_gid_offset 30
+#define	odc_gid_size 6
+#define	odc_nlink_offset 36
+#define	odc_nlink_size 6
+#define	odc_rdev_offset 42
+#define	odc_rdev_size 6
+#define	odc_mtime_offset 48
+#define	odc_mtime_size 11
+#define	odc_namesize_offset 59
+#define	odc_namesize_size 6
+#define	odc_filesize_offset 65
+#define	odc_filesize_size 11
+#define	odc_header_size 76
 
-struct cpio_newc_header {
-	char	c_magic[6];
-	char	c_ino[8];
-	char	c_mode[8];
-	char	c_uid[8];
-	char	c_gid[8];
-	char	c_nlink[8];
-	char	c_mtime[8];
-	char	c_filesize[8];
-	char	c_devmajor[8];
-	char	c_devminor[8];
-	char	c_rdevmajor[8];
-	char	c_rdevminor[8];
-	char	c_namesize[8];
-	char	c_crc[8];
-} __packed;
+#define	newc_magic_offset 0
+#define	newc_magic_size 6
+#define	newc_ino_offset 6
+#define	newc_ino_size 8
+#define	newc_mode_offset 14
+#define	newc_mode_size 8
+#define	newc_uid_offset 22
+#define	newc_uid_size 8
+#define	newc_gid_offset 30
+#define	newc_gid_size 8
+#define	newc_nlink_offset 38
+#define	newc_nlink_size 8
+#define	newc_mtime_offset 46
+#define	newc_mtime_size 8
+#define	newc_filesize_offset 54
+#define	newc_filesize_size 8
+#define	newc_devmajor_offset 62
+#define	newc_devmajor_size 8
+#define	newc_devminor_offset 70
+#define	newc_devminor_size 8
+#define	newc_rdevmajor_offset 78
+#define	newc_rdevmajor_size 8
+#define	newc_rdevminor_offset 86
+#define	newc_rdevminor_size 8
+#define	newc_namesize_offset 94
+#define	newc_namesize_size 8
+#define	newc_checksum_offset 102
+#define	newc_checksum_size 8
+#define	newc_header_size 110
 
-#ifdef _MSC_VER
-#undef __packed
-#pragma pack(pop)
-#endif
+/*
+ * An afio large ASCII header, which they named itself.
+ * afio utility uses this header, if a file size is larger than 2G bytes
+ * or inode/uid/gid is bigger than 65535(0xFFFF) or mtime is bigger than
+ * 0x7fffffff, which we cannot record to odc header because of its limit.
+ * If not, uses odc header.
+ */
+#define	afiol_magic_offset 0
+#define	afiol_magic_size 6
+#define	afiol_dev_offset 6
+#define	afiol_dev_size 8	/* hex */
+#define	afiol_ino_offset 14
+#define	afiol_ino_size 16	/* hex */
+#define	afiol_ino_m_offset 30	/* 'm' */
+#define	afiol_mode_offset 31
+#define	afiol_mode_size 6	/* oct */
+#define	afiol_uid_offset 37
+#define	afiol_uid_size 8	/* hex */
+#define	afiol_gid_offset 45
+#define	afiol_gid_size 8	/* hex */
+#define	afiol_nlink_offset 53
+#define	afiol_nlink_size 8	/* hex */
+#define	afiol_rdev_offset 61
+#define	afiol_rdev_size 8	/* hex */
+#define	afiol_mtime_offset 69
+#define	afiol_mtime_size 16	/* hex */
+#define	afiol_mtime_n_offset 85	/* 'n' */
+#define	afiol_namesize_offset 86
+#define	afiol_namesize_size 4	/* hex */
+#define	afiol_flag_offset 90
+#define	afiol_flag_size 4	/* hex */
+#define	afiol_xsize_offset 94
+#define	afiol_xsize_size 4	/* hex */
+#define	afiol_xsize_s_offset 98	/* 's' */
+#define	afiol_filesize_offset 99
+#define	afiol_filesize_size 16	/* hex */
+#define	afiol_filesize_c_offset 115	/* ':' */
+#define afiol_header_size 116
+
 
 struct links_entry {
         struct links_entry      *next;
@@ -111,21 +177,27 @@
 	int			(*read_header)(struct archive_read *, struct cpio *,
 				     struct archive_entry *, size_t *, size_t *);
 	struct links_entry	 *links_head;
-	struct archive_string	  entry_name;
-	struct archive_string	  entry_linkname;
-	off_t			  entry_bytes_remaining;
-	off_t			  entry_offset;
-	off_t			  entry_padding;
+	int64_t			  entry_bytes_remaining;
+	int64_t			  entry_bytes_unconsumed;
+	int64_t			  entry_offset;
+	int64_t			  entry_padding;
+
+	struct archive_string_conv *opt_sconv;
+	struct archive_string_conv *sconv_default;
+	int			  init_default_conversion;
 };
 
 static int64_t	atol16(const char *, unsigned);
 static int64_t	atol8(const char *, unsigned);
-static int	archive_read_format_cpio_bid(struct archive_read *);
+static int	archive_read_format_cpio_bid(struct archive_read *, int);
+static int	archive_read_format_cpio_options(struct archive_read *,
+		    const char *, const char *);
 static int	archive_read_format_cpio_cleanup(struct archive_read *);
 static int	archive_read_format_cpio_read_data(struct archive_read *,
-		    const void **, size_t *, off_t *);
+		    const void **, size_t *, int64_t *);
 static int	archive_read_format_cpio_read_header(struct archive_read *,
 		    struct archive_entry *);
+static int	archive_read_format_cpio_skip(struct archive_read *);
 static int	be4(const unsigned char *);
 static int	find_odc_header(struct archive_read *);
 static int	find_newc_header(struct archive_read *);
@@ -137,10 +209,13 @@
 		    struct archive_entry *, size_t *, size_t *);
 static int	header_odc(struct archive_read *, struct cpio *,
 		    struct archive_entry *, size_t *, size_t *);
+static int	header_afiol(struct archive_read *, struct cpio *,
+		    struct archive_entry *, size_t *, size_t *);
 static int	is_octal(const char *, size_t);
 static int	is_hex(const char *, size_t);
 static int	le4(const unsigned char *);
-static void	record_hardlink(struct cpio *cpio, struct archive_entry *entry);
+static int	record_hardlink(struct archive_read *a,
+		    struct cpio *cpio, struct archive_entry *entry);
 
 int
 archive_read_support_format_cpio(struct archive *_a)
@@ -149,22 +224,24 @@
 	struct cpio *cpio;
 	int r;
 
-	cpio = (struct cpio *)malloc(sizeof(*cpio));
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_cpio");
+
+	cpio = (struct cpio *)calloc(1, sizeof(*cpio));
 	if (cpio == NULL) {
 		archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
 		return (ARCHIVE_FATAL);
 	}
-	memset(cpio, 0, sizeof(*cpio));
 	cpio->magic = CPIO_MAGIC;
 
 	r = __archive_read_register_format(a,
 	    cpio,
 	    "cpio",
 	    archive_read_format_cpio_bid,
-	    NULL,
+	    archive_read_format_cpio_options,
 	    archive_read_format_cpio_read_header,
 	    archive_read_format_cpio_read_data,
-	    NULL,
+	    archive_read_format_cpio_skip,
 	    archive_read_format_cpio_cleanup);
 
 	if (r != ARCHIVE_OK)
@@ -174,19 +251,19 @@
 
 
 static int
-archive_read_format_cpio_bid(struct archive_read *a)
+archive_read_format_cpio_bid(struct archive_read *a, int best_bid)
 {
-	const void *h;
 	const unsigned char *p;
 	struct cpio *cpio;
 	int bid;
 
+	(void)best_bid; /* UNUSED */
+
 	cpio = (struct cpio *)(a->format->data);
 
-	if ((h = __archive_read_ahead(a, 6, NULL)) == NULL)
+	if ((p = __archive_read_ahead(a, 6, NULL)) == NULL)
 		return (-1);
 
-	p = (const unsigned char *)h;
 	bid = 0;
 	if (memcmp(p, "070707", 6) == 0) {
 		/* ASCII cpio archive (odc, POSIX.1) */
@@ -196,6 +273,14 @@
 		 * XXX TODO:  More verification; Could check that only octal
 		 * digits appear in appropriate header locations. XXX
 		 */
+	} else if (memcmp(p, "070727", 6) == 0) {
+		/* afio large ASCII cpio archive */
+		cpio->read_header = header_odc;
+		bid += 48;
+		/*
+		 * XXX TODO:  More verification; Could check that almost hex
+		 * digits appear in appropriate header locations. XXX
+		 */
 	} else if (memcmp(p, "070701", 6) == 0) {
 		/* ASCII cpio archive (SVR4 without CRC) */
 		cpio->read_header = header_newc;
@@ -230,16 +315,62 @@
 }
 
 static int
+archive_read_format_cpio_options(struct archive_read *a,
+    const char *key, const char *val)
+{
+	struct cpio *cpio;
+	int ret = ARCHIVE_FAILED;
+
+	cpio = (struct cpio *)(a->format->data);
+	if (strcmp(key, "compat-2x")  == 0) {
+		/* Handle filnames as libarchive 2.x */
+		cpio->init_default_conversion = (val != NULL)?1:0;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "cpio: hdrcharset option needs a character-set name");
+		else {
+			cpio->opt_sconv =
+			    archive_string_conversion_from_charset(
+				&a->archive, val, 0);
+			if (cpio->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
 archive_read_format_cpio_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
 	struct cpio *cpio;
 	const void *h;
+	struct archive_string_conv *sconv;
 	size_t namelength;
 	size_t name_pad;
 	int r;
 
 	cpio = (struct cpio *)(a->format->data);
+	sconv = cpio->opt_sconv;
+	if (sconv == NULL) {
+		if (!cpio->init_default_conversion) {
+			cpio->sconv_default =
+			    archive_string_default_conversion_for_read(
+			      &(a->archive));
+			cpio->init_default_conversion = 1;
+		}
+		sconv = cpio->sconv_default;
+	}
+	
 	r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad));
 
 	if (r < ARCHIVE_WARN)
@@ -249,20 +380,42 @@
 	h = __archive_read_ahead(a, namelength + name_pad, NULL);
 	if (h == NULL)
 	    return (ARCHIVE_FATAL);
+	if (archive_entry_copy_pathname_l(entry,
+	    (const char *)h, namelength, sconv) != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Pathname can't be converted from %s to current locale.",
+		    archive_string_conversion_charset_name(sconv));
+		r = ARCHIVE_WARN;
+	}
+	cpio->entry_offset = 0;
+
 	__archive_read_consume(a, namelength + name_pad);
-	archive_strncpy(&cpio->entry_name, (const char *)h, namelength);
-	archive_entry_set_pathname(entry, cpio->entry_name.s);
-	cpio->entry_offset = 0;
 
 	/* If this is a symlink, read the link contents. */
 	if (archive_entry_filetype(entry) == AE_IFLNK) {
 		h = __archive_read_ahead(a, cpio->entry_bytes_remaining, NULL);
 		if (h == NULL)
 			return (ARCHIVE_FATAL);
+		if (archive_entry_copy_symlink_l(entry, (const char *)h,
+		    cpio->entry_bytes_remaining, sconv) != 0) {
+			if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for Linkname");
+				return (ARCHIVE_FATAL);
+			}
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Linkname can't be converted from %s to "
+			    "current locale.",
+			    archive_string_conversion_charset_name(sconv));
+			r = ARCHIVE_WARN;
+		}
 		__archive_read_consume(a, cpio->entry_bytes_remaining);
-		archive_strncpy(&cpio->entry_linkname, (const char *)h,
-		    cpio->entry_bytes_remaining);
-		archive_entry_set_symlink(entry, cpio->entry_linkname.s);
 		cpio->entry_bytes_remaining = 0;
 	}
 
@@ -279,19 +432,27 @@
 	}
 
 	/* Detect and record hardlinks to previously-extracted entries. */
-	record_hardlink(cpio, entry);
+	if (record_hardlink(a, cpio, entry) != ARCHIVE_OK) {
+		return (ARCHIVE_FATAL);
+	}
 
 	return (r);
 }
 
 static int
 archive_read_format_cpio_read_data(struct archive_read *a,
-    const void **buff, size_t *size, off_t *offset)
+    const void **buff, size_t *size, int64_t *offset)
 {
 	ssize_t bytes_read;
 	struct cpio *cpio;
 
 	cpio = (struct cpio *)(a->format->data);
+
+	if (cpio->entry_bytes_unconsumed) {
+		__archive_read_consume(a, cpio->entry_bytes_unconsumed);
+		cpio->entry_bytes_unconsumed = 0;
+	}
+
 	if (cpio->entry_bytes_remaining > 0) {
 		*buff = __archive_read_ahead(a, 1, &bytes_read);
 		if (bytes_read <= 0)
@@ -299,21 +460,17 @@
 		if (bytes_read > cpio->entry_bytes_remaining)
 			bytes_read = cpio->entry_bytes_remaining;
 		*size = bytes_read;
+		cpio->entry_bytes_unconsumed = bytes_read;
 		*offset = cpio->entry_offset;
 		cpio->entry_offset += bytes_read;
 		cpio->entry_bytes_remaining -= bytes_read;
-		__archive_read_consume(a, bytes_read);
 		return (ARCHIVE_OK);
 	} else {
-		while (cpio->entry_padding > 0) {
-			*buff = __archive_read_ahead(a, 1, &bytes_read);
-			if (bytes_read <= 0)
-				return (ARCHIVE_FATAL);
-			if (bytes_read > cpio->entry_padding)
-				bytes_read = cpio->entry_padding;
-			__archive_read_consume(a, bytes_read);
-			cpio->entry_padding -= bytes_read;
+		if (cpio->entry_padding !=
+			__archive_read_consume(a, cpio->entry_padding)) {
+			return (ARCHIVE_FATAL);
 		}
+		cpio->entry_padding = 0;
 		*buff = NULL;
 		*size = 0;
 		*offset = cpio->entry_offset;
@@ -321,6 +478,22 @@
 	}
 }
 
+static int
+archive_read_format_cpio_skip(struct archive_read *a)
+{
+	struct cpio *cpio = (struct cpio *)(a->format->data);
+	int64_t to_skip = cpio->entry_bytes_remaining + cpio->entry_padding +
+		cpio->entry_bytes_unconsumed;
+
+	if (to_skip != __archive_read_consume(a, to_skip)) {
+		return (ARCHIVE_FATAL);
+	}
+	cpio->entry_bytes_remaining = 0;
+	cpio->entry_padding = 0;
+	cpio->entry_bytes_unconsumed = 0;
+	return (ARCHIVE_OK);
+}
+
 /*
  * Skip forward to the next cpio newc header by searching for the
  * 07070[12] string.  This should be generalized and merged with
@@ -349,7 +522,7 @@
 	ssize_t bytes;
 
 	for (;;) {
-		h = __archive_read_ahead(a, sizeof(struct cpio_newc_header), &bytes);
+		h = __archive_read_ahead(a, newc_header_size, &bytes);
 		if (h == NULL)
 			return (ARCHIVE_FATAL);
 		p = h;
@@ -358,19 +531,19 @@
 		/* Try the typical case first, then go into the slow search.*/
 		if (memcmp("07070", p, 5) == 0
 		    && (p[5] == '1' || p[5] == '2')
-		    && is_hex(p, sizeof(struct cpio_newc_header)))
+		    && is_hex(p, newc_header_size))
 			return (ARCHIVE_OK);
 
 		/*
 		 * Scan ahead until we find something that looks
-		 * like an odc header.
+		 * like a newc header.
 		 */
-		while (p + sizeof(struct cpio_newc_header) <= q) {
+		while (p + newc_header_size <= q) {
 			switch (p[5]) {
 			case '1':
 			case '2':
 				if (memcmp("07070", p, 5) == 0
-					&& is_hex(p, sizeof(struct cpio_newc_header))) {
+				    && is_hex(p, newc_header_size)) {
 					skip = p - (const char *)h;
 					__archive_read_consume(a, skip);
 					skipped += skip;
@@ -405,7 +578,7 @@
     struct archive_entry *entry, size_t *namelength, size_t *name_pad)
 {
 	const void *h;
-	const struct cpio_newc_header *header;
+	const char *header;
 	int r;
 
 	r = find_newc_header(a);
@@ -413,35 +586,34 @@
 		return (r);
 
 	/* Read fixed-size portion of header. */
-	h = __archive_read_ahead(a, sizeof(struct cpio_newc_header), NULL);
+	h = __archive_read_ahead(a, newc_header_size, NULL);
 	if (h == NULL)
 	    return (ARCHIVE_FATAL);
-	__archive_read_consume(a, sizeof(struct cpio_newc_header));
 
 	/* Parse out hex fields. */
-	header = (const struct cpio_newc_header *)h;
+	header = (const char *)h;
 
-	if (memcmp(header->c_magic, "070701", 6) == 0) {
+	if (memcmp(header + newc_magic_offset, "070701", 6) == 0) {
 		a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
 		a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)";
-	} else if (memcmp(header->c_magic, "070702", 6) == 0) {
+	} else if (memcmp(header + newc_magic_offset, "070702", 6) == 0) {
 		a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC;
 		a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)";
 	} else {
 		/* TODO: Abort here? */
 	}
 
-	archive_entry_set_devmajor(entry, atol16(header->c_devmajor, sizeof(header->c_devmajor)));
-	archive_entry_set_devminor(entry, atol16(header->c_devminor, sizeof(header->c_devminor)));
-	archive_entry_set_ino(entry, atol16(header->c_ino, sizeof(header->c_ino)));
-	archive_entry_set_mode(entry, atol16(header->c_mode, sizeof(header->c_mode)));
-	archive_entry_set_uid(entry, atol16(header->c_uid, sizeof(header->c_uid)));
-	archive_entry_set_gid(entry, atol16(header->c_gid, sizeof(header->c_gid)));
-	archive_entry_set_nlink(entry, atol16(header->c_nlink, sizeof(header->c_nlink)));
-	archive_entry_set_rdevmajor(entry, atol16(header->c_rdevmajor, sizeof(header->c_rdevmajor)));
-	archive_entry_set_rdevminor(entry, atol16(header->c_rdevminor, sizeof(header->c_rdevminor)));
-	archive_entry_set_mtime(entry, atol16(header->c_mtime, sizeof(header->c_mtime)), 0);
-	*namelength = atol16(header->c_namesize, sizeof(header->c_namesize));
+	archive_entry_set_devmajor(entry, atol16(header + newc_devmajor_offset, newc_devmajor_size));
+	archive_entry_set_devminor(entry, atol16(header + newc_devminor_offset, newc_devminor_size));
+	archive_entry_set_ino(entry, atol16(header + newc_ino_offset, newc_ino_size));
+	archive_entry_set_mode(entry, atol16(header + newc_mode_offset, newc_mode_size));
+	archive_entry_set_uid(entry, atol16(header + newc_uid_offset, newc_uid_size));
+	archive_entry_set_gid(entry, atol16(header + newc_gid_offset, newc_gid_size));
+	archive_entry_set_nlink(entry, atol16(header + newc_nlink_offset, newc_nlink_size));
+	archive_entry_set_rdevmajor(entry, atol16(header + newc_rdevmajor_offset, newc_rdevmajor_size));
+	archive_entry_set_rdevminor(entry, atol16(header + newc_rdevminor_offset, newc_rdevminor_size));
+	archive_entry_set_mtime(entry, atol16(header + newc_mtime_offset, newc_mtime_size), 0);
+	*namelength = atol16(header + newc_namesize_offset, newc_namesize_size);
 	/* Pad name to 2 more than a multiple of 4. */
 	*name_pad = (2 - *namelength) & 3;
 
@@ -451,10 +623,11 @@
 	 * size.
 	 */
 	cpio->entry_bytes_remaining =
-	    atol16(header->c_filesize, sizeof(header->c_filesize));
+	    atol16(header + newc_filesize_offset, newc_filesize_size);
 	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
 	/* Pad file contents to a multiple of 4. */
 	cpio->entry_padding = 3 & -cpio->entry_bytes_remaining;
+	__archive_read_consume(a, newc_header_size);
 	return (r);
 }
 
@@ -476,6 +649,27 @@
 }
 
 static int
+is_afio_large(const char *h, size_t len)
+{
+	if (len < afiol_header_size)
+		return (0);
+	if (h[afiol_ino_m_offset] != 'm'
+	    || h[afiol_mtime_n_offset] != 'n'
+	    || h[afiol_xsize_s_offset] != 's'
+	    || h[afiol_filesize_c_offset] != ':')
+		return (0);
+	if (!is_hex(h + afiol_dev_offset, afiol_ino_m_offset - afiol_dev_offset))
+		return (0);
+	if (!is_hex(h + afiol_mode_offset, afiol_mtime_n_offset - afiol_mode_offset))
+		return (0);
+	if (!is_hex(h + afiol_namesize_offset, afiol_xsize_s_offset - afiol_namesize_offset))
+		return (0);
+	if (!is_hex(h + afiol_filesize_offset, afiol_filesize_size))
+		return (0);
+	return (1);
+}
+
+static int
 find_odc_header(struct archive_read *a)
 {
 	const void *h;
@@ -484,29 +678,37 @@
 	ssize_t bytes;
 
 	for (;;) {
-		h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), &bytes);
+		h = __archive_read_ahead(a, odc_header_size, &bytes);
 		if (h == NULL)
 			return (ARCHIVE_FATAL);
 		p = h;
 		q = p + bytes;
 
 		/* Try the typical case first, then go into the slow search.*/
-		if (memcmp("070707", p, 6) == 0
-		    && is_octal(p, sizeof(struct cpio_odc_header)))
+		if (memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size))
 			return (ARCHIVE_OK);
+		if (memcmp("070727", p, 6) == 0 && is_afio_large(p, bytes)) {
+			a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE;
+			return (ARCHIVE_OK);
+		}
 
 		/*
 		 * Scan ahead until we find something that looks
 		 * like an odc header.
 		 */
-		while (p + sizeof(struct cpio_odc_header) <= q) {
+		while (p + odc_header_size <= q) {
 			switch (p[5]) {
 			case '7':
-				if (memcmp("070707", p, 6) == 0
-					&& is_octal(p, sizeof(struct cpio_odc_header))) {
+				if ((memcmp("070707", p, 6) == 0
+				    && is_octal(p, odc_header_size))
+				    || (memcmp("070727", p, 6) == 0
+				        && is_afio_large(p, q - p))) {
 					skip = p - (const char *)h;
 					__archive_read_consume(a, skip);
 					skipped += skip;
+					if (p[4] == '2')
+						a->archive.archive_format =
+						    ARCHIVE_FORMAT_CPIO_AFIO_LARGE;
 					if (skipped > 0) {
 						archive_set_error(&a->archive,
 						    0,
@@ -539,7 +741,7 @@
 {
 	const void *h;
 	int r;
-	const struct cpio_odc_header *header;
+	const char *header;
 
 	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
 	a->archive.archive_format_name = "POSIX octet-oriented cpio";
@@ -549,24 +751,31 @@
 	if (r < ARCHIVE_WARN)
 		return (r);
 
+	if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_AFIO_LARGE) {
+		int r2 = (header_afiol(a, cpio, entry, namelength, name_pad));
+		if (r2 == ARCHIVE_OK)
+			return (r);
+		else
+			return (r2);
+	}
+
 	/* Read fixed-size portion of header. */
-	h = __archive_read_ahead(a, sizeof(struct cpio_odc_header), NULL);
+	h = __archive_read_ahead(a, odc_header_size, NULL);
 	if (h == NULL)
 	    return (ARCHIVE_FATAL);
-	__archive_read_consume(a, sizeof(struct cpio_odc_header));
 
 	/* Parse out octal fields. */
-	header = (const struct cpio_odc_header *)h;
+	header = (const char *)h;
 
-	archive_entry_set_dev(entry, atol8(header->c_dev, sizeof(header->c_dev)));
-	archive_entry_set_ino(entry, atol8(header->c_ino, sizeof(header->c_ino)));
-	archive_entry_set_mode(entry, atol8(header->c_mode, sizeof(header->c_mode)));
-	archive_entry_set_uid(entry, atol8(header->c_uid, sizeof(header->c_uid)));
-	archive_entry_set_gid(entry, atol8(header->c_gid, sizeof(header->c_gid)));
-	archive_entry_set_nlink(entry, atol8(header->c_nlink, sizeof(header->c_nlink)));
-	archive_entry_set_rdev(entry, atol8(header->c_rdev, sizeof(header->c_rdev)));
-	archive_entry_set_mtime(entry, atol8(header->c_mtime, sizeof(header->c_mtime)), 0);
-	*namelength = atol8(header->c_namesize, sizeof(header->c_namesize));
+	archive_entry_set_dev(entry, atol8(header + odc_dev_offset, odc_dev_size));
+	archive_entry_set_ino(entry, atol8(header + odc_ino_offset, odc_ino_size));
+	archive_entry_set_mode(entry, atol8(header + odc_mode_offset, odc_mode_size));
+	archive_entry_set_uid(entry, atol8(header + odc_uid_offset, odc_uid_size));
+	archive_entry_set_gid(entry, atol8(header + odc_gid_offset, odc_gid_size));
+	archive_entry_set_nlink(entry, atol8(header + odc_nlink_offset, odc_nlink_size));
+	archive_entry_set_rdev(entry, atol8(header + odc_rdev_offset, odc_rdev_size));
+	archive_entry_set_mtime(entry, atol8(header + odc_mtime_offset, odc_mtime_size), 0);
+	*namelength = atol8(header + odc_namesize_offset, odc_namesize_size);
 	*name_pad = 0; /* No padding of filename. */
 
 	/*
@@ -575,45 +784,91 @@
 	 * size.
 	 */
 	cpio->entry_bytes_remaining =
-	    atol8(header->c_filesize, sizeof(header->c_filesize));
+	    atol8(header + odc_filesize_offset, odc_filesize_size);
 	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
 	cpio->entry_padding = 0;
+	__archive_read_consume(a, odc_header_size);
 	return (r);
 }
 
+/*
+ * NOTE: if a filename suffix is ".z", it is the file gziped by afio.
+ * it would be nice that we can show uncompressed file size and we can
+ * uncompressed file contents automatically, unfortunately we have nothing
+ * to get a uncompressed file size while reading each header. it means
+ * we also cannot uncompressed file contens under the our framework.
+ */
+static int
+header_afiol(struct archive_read *a, struct cpio *cpio,
+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+	const void *h;
+	const char *header;
+
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE;
+	a->archive.archive_format_name = "afio large ASCII";
+
+	/* Read fixed-size portion of header. */
+	h = __archive_read_ahead(a, afiol_header_size, NULL);
+	if (h == NULL)
+	    return (ARCHIVE_FATAL);
+
+	/* Parse out octal fields. */
+	header = (const char *)h;
+
+	archive_entry_set_dev(entry, atol16(header + afiol_dev_offset, afiol_dev_size));
+	archive_entry_set_ino(entry, atol16(header + afiol_ino_offset, afiol_ino_size));
+	archive_entry_set_mode(entry, atol8(header + afiol_mode_offset, afiol_mode_size));
+	archive_entry_set_uid(entry, atol16(header + afiol_uid_offset, afiol_uid_size));
+	archive_entry_set_gid(entry, atol16(header + afiol_gid_offset, afiol_gid_size));
+	archive_entry_set_nlink(entry, atol16(header + afiol_nlink_offset, afiol_nlink_size));
+	archive_entry_set_rdev(entry, atol16(header + afiol_rdev_offset, afiol_rdev_size));
+	archive_entry_set_mtime(entry, atol16(header + afiol_mtime_offset, afiol_mtime_size), 0);
+	*namelength = atol16(header + afiol_namesize_offset, afiol_namesize_size);
+	*name_pad = 0; /* No padding of filename. */
+
+	cpio->entry_bytes_remaining =
+	    atol16(header + afiol_filesize_offset, afiol_filesize_size);
+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+	cpio->entry_padding = 0;
+	__archive_read_consume(a, afiol_header_size);
+	return (ARCHIVE_OK);
+}
+
+
 static int
 header_bin_le(struct archive_read *a, struct cpio *cpio,
     struct archive_entry *entry, size_t *namelength, size_t *name_pad)
 {
 	const void *h;
-	const struct cpio_bin_header *header;
+	const unsigned char *header;
 
 	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE;
 	a->archive.archive_format_name = "cpio (little-endian binary)";
 
 	/* Read fixed-size portion of header. */
-	h = __archive_read_ahead(a, sizeof(struct cpio_bin_header), NULL);
+	h = __archive_read_ahead(a, bin_header_size, NULL);
 	if (h == NULL)
 	    return (ARCHIVE_FATAL);
-	__archive_read_consume(a, sizeof(struct cpio_bin_header));
 
 	/* Parse out binary fields. */
-	header = (const struct cpio_bin_header *)h;
+	header = (const unsigned char *)h;
 
-	archive_entry_set_dev(entry, header->c_dev[0] + header->c_dev[1] * 256);
-	archive_entry_set_ino(entry, header->c_ino[0] + header->c_ino[1] * 256);
-	archive_entry_set_mode(entry, header->c_mode[0] + header->c_mode[1] * 256);
-	archive_entry_set_uid(entry, header->c_uid[0] + header->c_uid[1] * 256);
-	archive_entry_set_gid(entry, header->c_gid[0] + header->c_gid[1] * 256);
-	archive_entry_set_nlink(entry, header->c_nlink[0] + header->c_nlink[1] * 256);
-	archive_entry_set_rdev(entry, header->c_rdev[0] + header->c_rdev[1] * 256);
-	archive_entry_set_mtime(entry, le4(header->c_mtime), 0);
-	*namelength = header->c_namesize[0] + header->c_namesize[1] * 256;
+	archive_entry_set_dev(entry, header[bin_dev_offset] + header[bin_dev_offset + 1] * 256);
+	archive_entry_set_ino(entry, header[bin_ino_offset] + header[bin_ino_offset + 1] * 256);
+	archive_entry_set_mode(entry, header[bin_mode_offset] + header[bin_mode_offset + 1] * 256);
+	archive_entry_set_uid(entry, header[bin_uid_offset] + header[bin_uid_offset + 1] * 256);
+	archive_entry_set_gid(entry, header[bin_gid_offset] + header[bin_gid_offset + 1] * 256);
+	archive_entry_set_nlink(entry, header[bin_nlink_offset] + header[bin_nlink_offset + 1] * 256);
+	archive_entry_set_rdev(entry, header[bin_rdev_offset] + header[bin_rdev_offset + 1] * 256);
+	archive_entry_set_mtime(entry, le4(header + bin_mtime_offset), 0);
+	*namelength = header[bin_namesize_offset] + header[bin_namesize_offset + 1] * 256;
 	*name_pad = *namelength & 1; /* Pad to even. */
 
-	cpio->entry_bytes_remaining = le4(header->c_filesize);
+	cpio->entry_bytes_remaining = le4(header + bin_filesize_offset);
 	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
 	cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
+	__archive_read_consume(a, bin_header_size);
 	return (ARCHIVE_OK);
 }
 
@@ -622,33 +877,34 @@
     struct archive_entry *entry, size_t *namelength, size_t *name_pad)
 {
 	const void *h;
-	const struct cpio_bin_header *header;
+	const unsigned char *header;
 
 	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE;
 	a->archive.archive_format_name = "cpio (big-endian binary)";
 
 	/* Read fixed-size portion of header. */
-	h = __archive_read_ahead(a, sizeof(struct cpio_bin_header), NULL);
+	h = __archive_read_ahead(a, bin_header_size, NULL);
 	if (h == NULL)
 	    return (ARCHIVE_FATAL);
-	__archive_read_consume(a, sizeof(struct cpio_bin_header));
 
 	/* Parse out binary fields. */
-	header = (const struct cpio_bin_header *)h;
-	archive_entry_set_dev(entry, header->c_dev[0] * 256 + header->c_dev[1]);
-	archive_entry_set_ino(entry, header->c_ino[0] * 256 + header->c_ino[1]);
-	archive_entry_set_mode(entry, header->c_mode[0] * 256 + header->c_mode[1]);
-	archive_entry_set_uid(entry, header->c_uid[0] * 256 + header->c_uid[1]);
-	archive_entry_set_gid(entry, header->c_gid[0] * 256 + header->c_gid[1]);
-	archive_entry_set_nlink(entry, header->c_nlink[0] * 256 + header->c_nlink[1]);
-	archive_entry_set_rdev(entry, header->c_rdev[0] * 256 + header->c_rdev[1]);
-	archive_entry_set_mtime(entry, be4(header->c_mtime), 0);
-	*namelength = header->c_namesize[0] * 256 + header->c_namesize[1];
+	header = (const unsigned char *)h;
+
+	archive_entry_set_dev(entry, header[bin_dev_offset] * 256 + header[bin_dev_offset + 1]);
+	archive_entry_set_ino(entry, header[bin_ino_offset] * 256 + header[bin_ino_offset + 1]);
+	archive_entry_set_mode(entry, header[bin_mode_offset] * 256 + header[bin_mode_offset + 1]);
+	archive_entry_set_uid(entry, header[bin_uid_offset] * 256 + header[bin_uid_offset + 1]);
+	archive_entry_set_gid(entry, header[bin_gid_offset] * 256 + header[bin_gid_offset + 1]);
+	archive_entry_set_nlink(entry, header[bin_nlink_offset] * 256 + header[bin_nlink_offset + 1]);
+	archive_entry_set_rdev(entry, header[bin_rdev_offset] * 256 + header[bin_rdev_offset + 1]);
+	archive_entry_set_mtime(entry, be4(header + bin_mtime_offset), 0);
+	*namelength = header[bin_namesize_offset] * 256 + header[bin_namesize_offset + 1];
 	*name_pad = *namelength & 1; /* Pad to even. */
 
-	cpio->entry_bytes_remaining = be4(header->c_filesize);
+	cpio->entry_bytes_remaining = be4(header + bin_filesize_offset);
 	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
 	cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
+	    __archive_read_consume(a, bin_header_size);
 	return (ARCHIVE_OK);
 }
 
@@ -667,7 +923,6 @@
                 free(cpio->links_head);
                 cpio->links_head = lp;
         }
-	archive_string_free(&cpio->entry_name);
 	free(cpio);
 	(a->format->data) = NULL;
 	return (ARCHIVE_OK);
@@ -733,15 +988,16 @@
 	return (l);
 }
 
-static void
-record_hardlink(struct cpio *cpio, struct archive_entry *entry)
+static int
+record_hardlink(struct archive_read *a,
+    struct cpio *cpio, struct archive_entry *entry)
 {
 	struct links_entry      *le;
 	dev_t dev;
 	int64_t ino;
 
 	if (archive_entry_nlink(entry) <= 1)
-		return;
+		return (ARCHIVE_OK);
 
 	dev = archive_entry_dev(entry);
 	ino = archive_entry_ino64(entry);
@@ -765,13 +1021,16 @@
 				free(le);
 			}
 
-			return;
+			return (ARCHIVE_OK);
 		}
 	}
 
 	le = (struct links_entry *)malloc(sizeof(struct links_entry));
-	if (le == NULL)
-		__archive_errx(1, "Out of memory adding file to list");
+	if (le == NULL) {
+		archive_set_error(&a->archive,
+		    ENOMEM, "Out of memory adding file to list");
+		return (ARCHIVE_FATAL);
+	}
 	if (cpio->links_head != NULL)
 		cpio->links_head->previous = le;
 	le->next = cpio->links_head;
@@ -781,6 +1040,11 @@
 	le->ino = ino;
 	le->links = archive_entry_nlink(entry) - 1;
 	le->name = strdup(archive_entry_pathname(entry));
-	if (le->name == NULL)
-		__archive_errx(1, "Out of memory adding file to list");
+	if (le->name == NULL) {
+		archive_set_error(&a->archive,
+		    ENOMEM, "Out of memory adding file to list");
+		return (ARCHIVE_FATAL);
+	}
+
+	return (ARCHIVE_OK);
 }
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_support_format_empty.c
--- a/head/contrib/libarchive/libarchive/archive_read_support_format_empty.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_support_format_empty.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,16 +24,16 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_empty.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_empty.c 232153 2012-02-25 10:58:02Z mm $");
 
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
 
-static int	archive_read_format_empty_bid(struct archive_read *);
+static int	archive_read_format_empty_bid(struct archive_read *, int);
 static int	archive_read_format_empty_read_data(struct archive_read *,
-		    const void **, size_t *, off_t *);
+		    const void **, size_t *, int64_t *);
 static int	archive_read_format_empty_read_header(struct archive_read *,
 		    struct archive_entry *);
 int
@@ -42,6 +42,9 @@
 	struct archive_read *a = (struct archive_read *)_a;
 	int r;
 
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_empty");
+
 	r = __archive_read_register_format(a,
 	    NULL,
 	    NULL,
@@ -57,14 +60,11 @@
 
 
 static int
-archive_read_format_empty_bid(struct archive_read *a)
+archive_read_format_empty_bid(struct archive_read *a, int best_bid)
 {
-	ssize_t avail;
-
-	(void)__archive_read_ahead(a, 1, &avail);
-	if (avail != 0)
-		return (-1);
-	return (1);
+	if (best_bid < 1 && __archive_read_ahead(a, 1, NULL) == NULL)
+		return (1);
+	return (-1);
 }
 
 static int
@@ -82,7 +82,7 @@
 
 static int
 archive_read_format_empty_read_data(struct archive_read *a,
-    const void **buff, size_t *size, off_t *offset)
+    const void **buff, size_t *size, int64_t *offset)
 {
 	(void)a; /* UNUSED */
 	(void)buff; /* UNUSED */
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c
--- a/head/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c	Fri Mar 02 16:54:40 2012 +0200
@@ -26,7 +26,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c 230759 2012-01-29 22:20:28Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -47,6 +47,7 @@
 #include "archive.h"
 #include "archive_endian.h"
 #include "archive_entry.h"
+#include "archive_entry_locale.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
 #include "archive_string.h"
@@ -285,6 +286,8 @@
 	int64_t		 number;
 	int		 nlinks;
 	struct archive_string name; /* Pathname */
+	unsigned char	*utf16be_name;
+	size_t		 utf16be_bytes;
 	char		 name_continues; /* Non-zero if name continues */
 	struct archive_string symlink;
 	char		 symlink_continues; /* Non-zero if link continues */
@@ -357,22 +360,34 @@
 		uint32_t	size;
 	} primary, joliet;
 
-	off_t	entry_sparse_offset;
+	int64_t	entry_sparse_offset;
 	int64_t	entry_bytes_remaining;
+	size_t  entry_bytes_unconsumed;
 	struct zisofs	 entry_zisofs;
 	struct content	*entry_content;
+	struct archive_string_conv *sconv_utf16be;
+	/*
+	 * Buffers for a full pathname in UTF-16BE in Joliet extensions.
+	 */
+#define UTF16_NAME_MAX	1024
+	unsigned char *utf16be_path;
+	size_t		 utf16be_path_len;
+	unsigned char *utf16be_previous_path;
+	size_t		 utf16be_previous_path_len;
 };
 
-static int	archive_read_format_iso9660_bid(struct archive_read *);
+static int	archive_read_format_iso9660_bid(struct archive_read *, int);
 static int	archive_read_format_iso9660_options(struct archive_read *,
 		    const char *, const char *);
 static int	archive_read_format_iso9660_cleanup(struct archive_read *);
 static int	archive_read_format_iso9660_read_data(struct archive_read *,
-		    const void **, size_t *, off_t *);
+		    const void **, size_t *, int64_t *);
 static int	archive_read_format_iso9660_read_data_skip(struct archive_read *);
 static int	archive_read_format_iso9660_read_header(struct archive_read *,
 		    struct archive_entry *);
 static const char *build_pathname(struct archive_string *, struct file_info *);
+static int	build_pathname_utf16be(unsigned char *, size_t, size_t *,
+		    struct file_info *);
 #if DEBUG
 static void	dump_isodirrec(FILE *, const unsigned char *isodirrec);
 #endif
@@ -388,8 +403,8 @@
 static int	isPVD(struct iso9660 *, const unsigned char *);
 static int	next_cache_entry(struct archive_read *, struct iso9660 *,
 		    struct file_info **);
-static int	next_entry_seek(struct archive_read *a, struct iso9660 *iso9660,
-		    struct file_info **pfile);
+static int	next_entry_seek(struct archive_read *, struct iso9660 *,
+		    struct file_info **);
 static struct file_info *
 		parse_file_info(struct archive_read *a,
 		    struct file_info *parent, const unsigned char *isodirrec);
@@ -417,12 +432,12 @@
 static inline void cache_add_entry(struct iso9660 *iso9660,
 		    struct file_info *file);
 static inline struct file_info *cache_get_entry(struct iso9660 *iso9660);
-static void	heap_add_entry(struct heap_queue *heap,
+static int	heap_add_entry(struct archive_read *a, struct heap_queue *heap,
 		    struct file_info *file, uint64_t key);
 static struct file_info *heap_get_entry(struct heap_queue *heap);
 
-#define add_entry(iso9660, file)	\
-	heap_add_entry(&((iso9660)->pending_files), file, file->offset)
+#define add_entry(arch, iso9660, file)	\
+	heap_add_entry(arch, &((iso9660)->pending_files), file, file->offset)
 #define next_entry(iso9660)		\
 	heap_get_entry(&((iso9660)->pending_files))
 
@@ -433,12 +448,15 @@
 	struct iso9660 *iso9660;
 	int r;
 
-	iso9660 = (struct iso9660 *)malloc(sizeof(*iso9660));
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_iso9660");
+
+	iso9660 = (struct iso9660 *)calloc(1, sizeof(*iso9660));
 	if (iso9660 == NULL) {
-		archive_set_error(&a->archive, ENOMEM, "Can't allocate iso9660 data");
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate iso9660 data");
 		return (ARCHIVE_FATAL);
 	}
-	memset(iso9660, 0, sizeof(*iso9660));
 	iso9660->magic = ISO9660_MAGIC;
 	iso9660->cache_files.first = NULL;
 	iso9660->cache_files.last = &(iso9660->cache_files.first);
@@ -468,14 +486,18 @@
 
 
 static int
-archive_read_format_iso9660_bid(struct archive_read *a)
+archive_read_format_iso9660_bid(struct archive_read *a, int best_bid)
 {
 	struct iso9660 *iso9660;
 	ssize_t bytes_read;
-	const void *h;
 	const unsigned char *p;
 	int seenTerminator;
 
+	/* If there's already a better bid than we can ever
+	   make, don't bother testing. */
+	if (best_bid > 48)
+		return (-1);
+
 	iso9660 = (struct iso9660 *)(a->format->data);
 
 	/*
@@ -484,12 +506,11 @@
 	 * if the I/O layer gives us more, we'll take it.
 	 */
 #define RESERVED_AREA	(SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE)
-	h = __archive_read_ahead(a,
+	p = __archive_read_ahead(a,
 	    RESERVED_AREA + 8 * LOGICAL_BLOCK_SIZE,
 	    &bytes_read);
-	if (h == NULL)
+	if (p == NULL)
 	    return (-1);
-	p = (const unsigned char *)h;
 
 	/* Skip the reserved area. */
 	bytes_read -= RESERVED_AREA;
@@ -505,10 +526,8 @@
 		/* Standard Identifier must be "CD001" */
 		if (memcmp(p + 1, "CD001", 5) != 0)
 			return (0);
-		if (!iso9660->primary.location) {
-			if (isPVD(iso9660, p))
-				continue;
-		}
+		if (isPVD(iso9660, p))
+			continue;
 		if (!iso9660->joliet.location) {
 			if (isJolietSVD(iso9660, p))
 				continue;
@@ -564,7 +583,7 @@
 
 	/* Note: The "warn" return is just to inform the options
 	 * supervisor that we didn't handle it.  It will generate
-	 * a suitable error if noone used this option. */
+	 * a suitable error if no one used this option. */
 	return (ARCHIVE_WARN);
 }
 
@@ -893,10 +912,10 @@
 		return (0);
 
 	/* Reserved field must be 0. */
-	/* FreeBSD: makefs erroneously created images with 0x20 */
+	/* But accept NetBSD/FreeBSD "makefs" images with 0x20 here. */
 	for (i = 0; i < PVD_reserved4_size; ++i)
-		if (h[PVD_reserved4_offset + i] != 0 &&
-		    h[PVD_reserved4_offset + i] != 32)
+		if (h[PVD_reserved4_offset + i] != 0
+		    && h[PVD_reserved4_offset + i] != 0x20)
 			return (0);
 
 	/* Reserved field must be 0. */
@@ -912,11 +931,13 @@
 	if (p[DR_length_offset] != 34)
 		return (0);
 
-	iso9660->logical_block_size = logical_block_size;
-	iso9660->volume_block = volume_block;
-	iso9660->volume_size = logical_block_size * (uint64_t)volume_block;
-	iso9660->primary.location = archive_le32dec(p + DR_extent_offset);
-	iso9660->primary.size = archive_le32dec(p + DR_size_offset);
+	if (!iso9660->primary.location) {
+		iso9660->logical_block_size = logical_block_size;
+		iso9660->volume_block = volume_block;
+		iso9660->volume_size = logical_block_size * (uint64_t)volume_block;
+		iso9660->primary.location = archive_le32dec(p + DR_extent_offset);
+		iso9660->primary.size = archive_le32dec(p + DR_size_offset);
+	}
 
 	return (48);
 }
@@ -927,7 +948,7 @@
 	struct iso9660 *iso9660;
 	const unsigned char *b, *p;
 	struct file_info *multi;
-	size_t step;
+	size_t step, skip_size;
 
 	iso9660 = (struct iso9660 *)(a->format->data);
 	if (iso9660->current_position > parent->offset) {
@@ -948,7 +969,7 @@
 		int64_t skipsize;
 
 		skipsize = parent->offset - iso9660->current_position;
-		skipsize = __archive_read_skip(a, skipsize);
+		skipsize = __archive_read_consume(a, skipsize);
 		if (skipsize < 0)
 			return ((int)skipsize);
 		iso9660->current_position = parent->offset;
@@ -963,9 +984,9 @@
 		    "ISO9660 directory list");
 		return (ARCHIVE_FATAL);
 	}
-	__archive_read_consume(a, step);
 	iso9660->current_position += step;
 	multi = NULL;
+	skip_size = step;
 	while (step) {
 		p = b;
 		b += iso9660->logical_block_size;
@@ -987,8 +1008,10 @@
 			    && *(p + DR_name_offset) == '\001')
 				continue;
 			child = parse_file_info(a, parent, p);
-			if (child == NULL)
+			if (child == NULL) {
+				__archive_read_consume(a, skip_size);
 				return (ARCHIVE_FATAL);
+			}
 			if (child->cl_offset == 0 &&
 			    (child->multi_extent || multi != NULL)) {
 				struct content *con;
@@ -1003,8 +1026,8 @@
 				if (con == NULL) {
 					archive_set_error(
 					    &a->archive, ENOMEM,
-					    "No memory for "
-					    "multi extent");
+					    "No memory for multi extent");
+					__archive_read_consume(a, skip_size);
 					return (ARCHIVE_FATAL);
 				}
 				con->offset = child->offset;
@@ -1012,18 +1035,23 @@
 				con->next = NULL;
 				*multi->contents.last = con;
 				multi->contents.last = &(con->next);
-				if (multi == child)
-					add_entry(iso9660, child);
-				else {
+				if (multi == child) {
+					if (add_entry(a, iso9660, child)
+					    != ARCHIVE_OK)
+						return (ARCHIVE_FATAL);
+				} else {
 					multi->size += child->size;
 					if (!child->multi_extent)
 						multi = NULL;
 				}
 			} else
-				add_entry(iso9660, child);
+				if (add_entry(a, iso9660, child) != ARCHIVE_OK)
+					return (ARCHIVE_FATAL);
 		}
 	}
 
+	__archive_read_consume(a, skip_size);
+
 	/* Read data which recorded by RRIP "CE" extension. */
 	if (read_CE(a, iso9660) != ARCHIVE_OK)
 		return (ARCHIVE_FATAL);
@@ -1061,7 +1089,7 @@
 			vd = &(iso9660->joliet);
 
 		skipsize = LOGICAL_BLOCK_SIZE * vd->location;
-		skipsize = __archive_read_skip(a, skipsize);
+		skipsize = __archive_read_consume(a, skipsize);
 		if (skipsize < 0)
 			return ((int)skipsize);
 		iso9660->current_position = skipsize;
@@ -1099,7 +1127,7 @@
 			vd = &(iso9660->joliet);
 			skipsize = LOGICAL_BLOCK_SIZE * vd->location;
 			skipsize -= iso9660->current_position;
-			skipsize = __archive_read_skip(a, skipsize);
+			skipsize = __archive_read_consume(a, skipsize);
 			if (skipsize < 0)
 				return ((int)skipsize);
 			iso9660->current_position += skipsize;
@@ -1112,7 +1140,6 @@
 				    "ISO9660 directory list");
 				return (ARCHIVE_FATAL);
 			}
-			seenJoliet = iso9660->seenJoliet;/* Save flag. */
 			iso9660->seenJoliet = 0;
 			file = parse_file_info(a, NULL, block);
 			if (file == NULL)
@@ -1120,7 +1147,8 @@
 			iso9660->seenJoliet = seenJoliet;
 		}
 		/* Store the root directory in the pending list. */
-		add_entry(iso9660, file);
+		if (add_entry(a, iso9660, file) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
 		if (iso9660->seenRockridge) {
 			a->archive.archive_format =
 			    ARCHIVE_FORMAT_ISO9660_ROCKRIDGE;
@@ -1129,17 +1157,82 @@
 		}
 	}
 
+	file = NULL;/* Eliminate a warning. */
 	/* Get the next entry that appears after the current offset. */
 	r = next_entry_seek(a, iso9660, &file);
 	if (r != ARCHIVE_OK)
 		return (r);
 
+	if (iso9660->seenJoliet) {
+		/*
+		 * Convert UTF-16BE of a filename to local locale MBS
+		 * and store the result into a filename field.
+		 */
+		if (iso9660->sconv_utf16be == NULL) {
+			iso9660->sconv_utf16be =
+			    archive_string_conversion_from_charset(
+				&(a->archive), "UTF-16BE", 1);
+			if (iso9660->sconv_utf16be == NULL)
+				/* Coundn't allocate memory */
+				return (ARCHIVE_FATAL);
+		}
+		if (iso9660->utf16be_path == NULL) {
+			iso9660->utf16be_path = malloc(UTF16_NAME_MAX);
+			if (iso9660->utf16be_path == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "No memory");
+				return (ARCHIVE_FATAL);
+			}
+		}
+		if (iso9660->utf16be_previous_path == NULL) {
+			iso9660->utf16be_previous_path = malloc(UTF16_NAME_MAX);
+			if (iso9660->utf16be_previous_path == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "No memory");
+				return (ARCHIVE_FATAL);
+			}
+		}
+
+		iso9660->utf16be_path_len = 0;
+		if (build_pathname_utf16be(iso9660->utf16be_path,
+		    UTF16_NAME_MAX, &(iso9660->utf16be_path_len), file) != 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Pathname is too long");
+		}
+
+		r = archive_entry_copy_pathname_l(entry,
+		    (const char *)iso9660->utf16be_path,
+		    iso9660->utf16be_path_len,
+		    iso9660->sconv_utf16be);
+		if (r != 0) {
+			if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "No memory for Pathname");
+				return (ARCHIVE_FATAL);
+			}
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Pathname cannot be converted "
+			    "from %s to current locale.",
+			    archive_string_conversion_charset_name(
+			      iso9660->sconv_utf16be));
+
+			rd_r = ARCHIVE_WARN;
+		}
+	} else {
+		archive_string_empty(&iso9660->pathname);
+		archive_entry_set_pathname(entry,
+		    build_pathname(&iso9660->pathname, file));
+	}
+
 	iso9660->entry_bytes_remaining = file->size;
 	iso9660->entry_sparse_offset = 0; /* Offset for sparse-file-aware clients. */
 
 	if (file->offset + file->size > iso9660->volume_size) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "File is beyond end-of-media: %s", file->name.s);
+		    "File is beyond end-of-media: %s",
+		    archive_entry_pathname(entry));
 		iso9660->entry_bytes_remaining = 0;
 		iso9660->entry_sparse_offset = 0;
 		return (ARCHIVE_WARN);
@@ -1160,9 +1253,6 @@
 	/* N.B.: Rock Ridge supports 64-bit device numbers. */
 	archive_entry_set_rdev(entry, (dev_t)file->rdev);
 	archive_entry_set_size(entry, iso9660->entry_bytes_remaining);
-	archive_string_empty(&iso9660->pathname);
-	archive_entry_set_pathname(entry,
-	    build_pathname(&iso9660->pathname, file));
 	if (file->symlink.s != NULL)
 		archive_entry_copy_symlink(entry, file->symlink.s);
 
@@ -1172,12 +1262,32 @@
 	 * original entry. */
 	if (file->number != -1 &&
 	    file->number == iso9660->previous_number) {
-		archive_entry_set_hardlink(entry,
-		    iso9660->previous_pathname.s);
+		if (iso9660->seenJoliet) {
+			r = archive_entry_copy_hardlink_l(entry,
+			    (const char *)iso9660->utf16be_previous_path,
+			    iso9660->utf16be_previous_path_len,
+			    iso9660->sconv_utf16be);
+			if (r != 0) {
+				if (errno == ENOMEM) {
+					archive_set_error(&a->archive, ENOMEM,
+					    "No memory for Linkname");
+					return (ARCHIVE_FATAL);
+				}
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Linkname cannot be converted "
+				    "from %s to current locale.",
+				    archive_string_conversion_charset_name(
+				      iso9660->sconv_utf16be));
+				rd_r = ARCHIVE_WARN;
+			}
+		} else
+			archive_entry_set_hardlink(entry,
+			    iso9660->previous_pathname.s);
 		archive_entry_unset_size(entry);
 		iso9660->entry_bytes_remaining = 0;
 		iso9660->entry_sparse_offset = 0;
-		return (ARCHIVE_OK);
+		return (rd_r);
 	}
 
 	/* Except for the hardlink case above, if the offset of the
@@ -1198,7 +1308,8 @@
 	if ((file->mode & AE_IFMT) != AE_IFDIR &&
 	    file->offset < iso9660->current_position) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Ignoring out-of-order file (%s) %jd < %jd",
+		    "Ignoring out-of-order file @%jx (%s) %jd < %jd",
+		    (intmax_t)file->number,
 		    iso9660->pathname.s,
 		    (intmax_t)file->offset,
 		    (intmax_t)iso9660->current_position);
@@ -1226,7 +1337,13 @@
 	}
 
 	iso9660->previous_number = file->number;
-	archive_strcpy(&iso9660->previous_pathname, iso9660->pathname.s);
+	if (iso9660->seenJoliet) {
+		memcpy(iso9660->utf16be_previous_path, iso9660->utf16be_path,
+		    iso9660->utf16be_path_len);
+		iso9660->utf16be_previous_path_len = iso9660->utf16be_path_len;
+	} else
+		archive_strcpy(
+		    &iso9660->previous_pathname, iso9660->pathname.s);
 
 	/* Reset entry_bytes_remaining if the file is multi extent. */
 	iso9660->entry_content = file->contents.first;
@@ -1260,7 +1377,7 @@
 
 static int
 zisofs_read_data(struct archive_read *a,
-    const void **buff, size_t *size, off_t *offset)
+    const void **buff, size_t *size, int64_t *offset)
 {
 	struct iso9660 *iso9660;
 	struct zisofs  *zisofs;
@@ -1382,7 +1499,7 @@
 		}
 
 		if (!zisofs->initialized)
-			goto next_data; /* We need more datas. */
+			goto next_data; /* We need more data. */
 	}
 
 	/*
@@ -1393,21 +1510,26 @@
 
 		if (zisofs->block_off + 4 >= zisofs->block_pointers_size) {
 			/* There isn't a pair of offsets. */
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
 			    "Illegal zisofs block pointers");
 			return (ARCHIVE_FATAL);
 		}
-		bst = archive_le32dec(zisofs->block_pointers + zisofs->block_off);
+		bst = archive_le32dec(
+		    zisofs->block_pointers + zisofs->block_off);
 		if (bst != zisofs->pz_offset + (bytes_read - avail)) {
-			/* TODO: Should we seek offset of current file by bst ? */
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			/* TODO: Should we seek offset of current file
+			 * by bst ? */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
 			    "Illegal zisofs block pointers(cannot seek)");
 			return (ARCHIVE_FATAL);
 		}
 		bed = archive_le32dec(
 		    zisofs->block_pointers + zisofs->block_off + 4);
 		if (bed < bst) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
 			    "Illegal zisofs block pointers");
 			return (ARCHIVE_FATAL);
 		}
@@ -1430,7 +1552,7 @@
 	}
 
 	/*
-	 * Make uncompressed datas.
+	 * Make uncompressed data.
 	 */
 	if (zisofs->block_avail == 0) {
 		memset(zisofs->uncompressed_buffer, 0,
@@ -1469,7 +1591,7 @@
 	iso9660->entry_bytes_remaining -= bytes_read;
 	iso9660->current_position += bytes_read;
 	zisofs->pz_offset += bytes_read;
-	__archive_read_consume(a, bytes_read);
+	iso9660->entry_bytes_unconsumed += bytes_read;
 
 	return (ARCHIVE_OK);
 }
@@ -1478,7 +1600,7 @@
 
 static int
 zisofs_read_data(struct archive_read *a,
-    const void **buff, size_t *size, off_t *offset)
+    const void **buff, size_t *size, int64_t *offset)
 {
 
 	(void)buff;/* UNUSED */
@@ -1493,12 +1615,18 @@
 
 static int
 archive_read_format_iso9660_read_data(struct archive_read *a,
-    const void **buff, size_t *size, off_t *offset)
+    const void **buff, size_t *size, int64_t *offset)
 {
 	ssize_t bytes_read;
 	struct iso9660 *iso9660;
 
 	iso9660 = (struct iso9660 *)(a->format->data);
+
+	if (iso9660->entry_bytes_unconsumed) {
+		__archive_read_consume(a, iso9660->entry_bytes_unconsumed);
+		iso9660->entry_bytes_unconsumed = 0;
+	}
+
 	if (iso9660->entry_bytes_remaining <= 0) {
 		if (iso9660->entry_content != NULL)
 			iso9660->entry_content = iso9660->entry_content->next;
@@ -1514,7 +1642,7 @@
 
 			step = iso9660->entry_content->offset -
 			    iso9660->current_position;
-			step = __archive_read_skip(a, step);
+			step = __archive_read_consume(a, step);
 			if (step < 0)
 				return ((int)step);
 			iso9660->current_position =
@@ -1548,8 +1676,8 @@
 	*offset = iso9660->entry_sparse_offset;
 	iso9660->entry_sparse_offset += bytes_read;
 	iso9660->entry_bytes_remaining -= bytes_read;
+	iso9660->entry_bytes_unconsumed = bytes_read;
 	iso9660->current_position += bytes_read;
-	__archive_read_consume(a, bytes_read);
 	return (ARCHIVE_OK);
 }
 
@@ -1577,6 +1705,8 @@
 		}
 	}
 #endif
+	free(iso9660->utf16be_path);
+	free(iso9660->utf16be_previous_path);
 	free(iso9660);
 	(a->format->data) = NULL;
 	return (r);
@@ -1627,20 +1757,26 @@
 	if (location > 0 &&
 	    (location + ((fsize + iso9660->logical_block_size -1)
 	       / iso9660->logical_block_size))
-		> (uint32_t)iso9660->volume_block) {
+			> (uint32_t)iso9660->volume_block) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Invalid location of extent of file");
+		return (NULL);
+	}
+	/* Sanity check that location doesn't have a negative value
+	 * when the file is not empty. it's too large. */
+	if (fsize != 0 && location < 0) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Invalid location of extent of file");
 		return (NULL);
 	}
 
 	/* Create a new file entry and copy data from the ISO dir record. */
-	file = (struct file_info *)malloc(sizeof(*file));
+	file = (struct file_info *)calloc(1, sizeof(*file));
 	if (file == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
 		    "No memory for file entry");
 		return (NULL);
 	}
-	memset(file, 0, sizeof(*file));
 	file->parent = parent;
 	file->offset = iso9660->logical_block_size * (uint64_t)location;
 	file->size = fsize;
@@ -1661,24 +1797,13 @@
 		 * names which are 103 UCS2 characters(206 bytes) by their
 		 * option '-joliet-long'.
 		 */
-		wchar_t wbuff[103+1], *wp;
-		const unsigned char *c;
-
 		if (name_len > 206)
 			name_len = 206;
-		/* convert BE UTF-16 to wchar_t */
-		for (c = p, wp = wbuff;
-				c < (p + name_len) &&
-				wp < (wbuff + sizeof(wbuff)/sizeof(*wbuff) - 1);
-				c += 2) {
-			*wp++ = (((255 & (int)c[0]) << 8) | (255 & (int)c[1]));
-		}
-		*wp = L'\0';
+		name_len &= ~1;
 
-#if 0 /* untested code, is it at all useful on Joliet? */
 		/* trim trailing first version and dot from filename.
 		 *
-		 * Remember we where in UTF-16BE land!
+		 * Remember we were in UTF-16BE land!
 		 * SEPARATOR 1 (.) and SEPARATOR 2 (;) are both
 		 * 16 bits big endian characters on Joliet.
 		 *
@@ -1687,18 +1812,21 @@
 		 *       *, /, :, ;, ? and \.
 		 */
 		/* Chop off trailing ';1' from files. */
-		if (*(wp-2) == ';' && *(wp-1) == '1') {
-			wp-=2;
-			*wp = L'\0';
+		if (name_len > 4 && p[name_len-4] == 0 && p[name_len-3] == ';'
+		    && p[name_len-2] == 0 && p[name_len-1] == '1')
+			name_len -= 4;
+#if 0 /* XXX: this somehow manages to strip of single-character file extensions, like '.c'. */
+		/* Chop off trailing '.' from filenames. */
+		if (name_len > 2 && p[name_len-2] == 0 && p[name_len-1] == '.')
+			name_len -= 2;
+#endif
+		if ((file->utf16be_name = malloc(name_len)) == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "No memory for file name");
+			return (NULL);
 		}
-
-		/* Chop off trailing '.' from filenames. */
-		if (*(wp-1) == '.')
-			*(--wp) = L'\0';
-#endif
-
-		/* store the result in the file name field. */
-		archive_strappend_w_utf8(&file->name, wbuff);
+		memcpy(file->utf16be_name, p, name_len);
+		file->utf16be_bytes = name_len;
 	} else {
 		/* Chop off trailing ';1' from files. */
 		if (name_len > 2 && p[name_len - 2] == ';' &&
@@ -1721,22 +1849,24 @@
 	else
 		file->multi_extent = 0;
 	/*
-	 * Use location for file number.
-	 * File number is treated as inode number to find out harlink
-	 * target. If Rockridge extensions is being used, file number
-	 * will be overwritten by FILE SERIAL NUMBER of RRIP "PX"
-	 * extension.
-	 * NOTE: Old mkisofs did not record that FILE SERIAL NUMBER
+	 * Use a location for the file number, which is treated as an inode
+	 * number to find out hardlink target. If Rockridge extensions is
+	 * being used, the file number will be overwritten by FILE SERIAL
+	 * NUMBER of RRIP "PX" extension.
+	 * Note: Old mkisofs did not record that FILE SERIAL NUMBER
 	 * in ISO images.
+	 * Note2: xorriso set 0 to the location of a symlink file. 
 	 */
-	if (file->size == 0 && location >= 0)
-		/* If file->size is zero, its location points wrong place.
-		 * Dot not use it for file number.
-		 * When location has negative value, it can be used
-		 * for file number.
+	if (file->size == 0 && location >= 0) {
+		/* If file->size is zero, its location points wrong place,
+		 * and so we should not use it for the file number.
+		 * When the location has negative value, it can be used
+		 * for the file number.
 		 */
 		file->number = -1;
-	else
+		/* Do not appear before any directory entries. */
+		file->offset = -1;
+	} else
 		file->number = (int64_t)(uint32_t)location;
 
 	/* Rockridge extensions overwrite information from above. */
@@ -2123,9 +2253,13 @@
 	offset = ((uint64_t)location) * (uint64_t)iso9660->logical_block_size;
 	if (((file->mode & AE_IFMT) == AE_IFREG &&
 	    offset >= file->offset) ||
-	    offset < iso9660->current_position) {
+	    offset < iso9660->current_position ||
+	    (((uint64_t)file->ce_offset) + file->ce_size)
+	      > (uint64_t)iso9660->logical_block_size ||
+	    offset + file->ce_offset + file->ce_size
+		  > iso9660->volume_size) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Invalid location in SUSP \"CE\" extension");
+		    "Invalid parameter in SUSP \"CE\" extension");
 		return (ARCHIVE_FATAL);
 	}
 
@@ -2139,11 +2273,15 @@
 		else
 			new_size = heap->allocated * 2;
 		/* Overflow might keep us from growing the list. */
-		if (new_size <= heap->allocated)
-			__archive_errx(1, "Out of memory");
+		if (new_size <= heap->allocated) {
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
 		p = malloc(new_size * sizeof(p[0]));
-		if (p == NULL)
-			__archive_errx(1, "Out of memory");
+		if (p == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
 		if (heap->reqs != NULL) {
 			memcpy(p, heap->reqs, heap->cnt * sizeof(*p));
 			free(heap->reqs);
@@ -2164,7 +2302,7 @@
 			heap->reqs[hole].file = file;
 			return (ARCHIVE_OK);
 		}
-		// Move parent into hole <==> move hole up tree.
+		/* Move parent into hole <==> move hole up tree. */
 		heap->reqs[hole] = heap->reqs[parent];
 		hole = parent;
 	}
@@ -2191,14 +2329,14 @@
 	/*
 	 * Rebalance the heap.
 	 */
-	a = 0; // Starting element and its offset
+	a = 0; /* Starting element and its offset */
 	a_offset = heap->reqs[a].offset;
 	for (;;) {
-		b = a + a + 1; // First child
+		b = a + a + 1; /* First child */
 		if (b >= heap->cnt)
 			return;
 		b_offset = heap->reqs[b].offset;
-		c = b + 1; // Use second child if it is smaller.
+		c = b + 1; /* Use second child if it is smaller. */
 		if (c < heap->cnt) {
 			c_offset = heap->reqs[c].offset;
 			if (c_offset < b_offset) {
@@ -2240,6 +2378,12 @@
 		}
 		do {
 			file = heap->reqs[0].file;
+			if (file->ce_offset + file->ce_size > step) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Malformed CE information");
+				return (ARCHIVE_FATAL);
+			}
 			p = b + file->ce_offset;
 			end = p + file->ce_size;
 			next_CE(heap);
@@ -2280,12 +2424,14 @@
 	case 0:
 		if (data_length < 2)
 			return;
-		archive_strncat(&file->name, (const char *)data + 1, data_length - 1);
+		archive_strncat(&file->name,
+		    (const char *)data + 1, data_length - 1);
 		break;
 	case 1:
 		if (data_length < 2)
 			return;
-		archive_strncat(&file->name, (const char *)data + 1, data_length - 1);
+		archive_strncat(&file->name,
+		    (const char *)data + 1, data_length - 1);
 		file->name_continues = 1;
 		break;
 	case 2:
@@ -2496,6 +2642,7 @@
 
 		archive_string_free(&file->name);
 		archive_string_free(&file->symlink);
+		free(file->utf16be_name);
 		con = file->contents.first;
 		while (con != NULL) {
 			connext = con->next;
@@ -2523,12 +2670,19 @@
 	if (file->size == 0)
 		file->offset = iso9660->current_position;
 
+	/* flush any remaining bytes from the last round to ensure
+	 * we're positioned */
+	if (iso9660->entry_bytes_unconsumed) {
+		__archive_read_consume(a, iso9660->entry_bytes_unconsumed);
+		iso9660->entry_bytes_unconsumed = 0;
+	}
+
 	/* Seek forward to the start of the entry. */
 	if (iso9660->current_position < file->offset) {
 		int64_t step;
 
 		step = file->offset - iso9660->current_position;
-		step = __archive_read_skip(a, step);
+		step = __archive_read_consume(a, step);
 		if (step < 0)
 			return ((int)step);
 		iso9660->current_position = file->offset;
@@ -2819,13 +2973,15 @@
 	if ((file = iso9660->cache_files.first) != NULL) {
 		iso9660->cache_files.first = file->next;
 		if (iso9660->cache_files.first == NULL)
-			iso9660->cache_files.last = &(iso9660->cache_files.first);
+			iso9660->cache_files.last =
+			    &(iso9660->cache_files.first);
 	}
 	return (file);
 }
 
-static void
-heap_add_entry(struct heap_queue *heap, struct file_info *file, uint64_t key)
+static int
+heap_add_entry(struct archive_read *a, struct heap_queue *heap,
+    struct file_info *file, uint64_t key)
 {
 	uint64_t file_key, parent_key;
 	int hole, parent;
@@ -2838,12 +2994,18 @@
 		if (heap->allocated < 1024)
 			new_size = 1024;
 		/* Overflow might keep us from growing the list. */
-		if (new_size <= heap->allocated)
-			__archive_errx(1, "Out of memory");
+		if (new_size <= heap->allocated) {
+			archive_set_error(&a->archive,
+			    ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
 		new_pending_files = (struct file_info **)
 		    malloc(new_size * sizeof(new_pending_files[0]));
-		if (new_pending_files == NULL)
-			__archive_errx(1, "Out of memory");
+		if (new_pending_files == NULL) {
+			archive_set_error(&a->archive,
+			    ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
 		memcpy(new_pending_files, heap->files,
 		    heap->allocated * sizeof(new_pending_files[0]));
 		if (heap->files != NULL)
@@ -2863,13 +3025,15 @@
 		parent_key = heap->files[parent]->key;
 		if (file_key >= parent_key) {
 			heap->files[hole] = file;
-			return;
+			return (ARCHIVE_OK);
 		}
-		// Move parent into hole <==> move hole up tree.
+		/* Move parent into hole <==> move hole up tree. */
 		heap->files[hole] = heap->files[parent];
 		hole = parent;
 	}
 	heap->files[0] = file;
+
+	return (ARCHIVE_OK);
 }
 
 static struct file_info *
@@ -2895,14 +3059,14 @@
 	/*
 	 * Rebalance the heap.
 	 */
-	a = 0; // Starting element and its heap key
+	a = 0; /* Starting element and its heap key */
 	a_key = heap->files[a]->key;
 	for (;;) {
-		b = a + a + 1; // First child
+		b = a + a + 1; /* First child */
 		if (b >= heap->used)
 			return (r);
 		b_key = heap->files[b]->key;
-		c = b + 1; // Use second child if it is smaller.
+		c = b + 1; /* Use second child if it is smaller. */
 		if (c < heap->used) {
 			c_key = heap->files[c]->key;
 			if (c_key < b_key) {
@@ -2980,6 +3144,8 @@
 #if HAVE_TIMEGM
 	/* Use platform timegm() if available. */
 	return (timegm(t));
+#elif HAVE__MKGMTIME64
+	return (_mkgmtime64(t));
 #else
 	/* Else use direct calculation using POSIX assumptions. */
 	/* First, fix up tm_yday based on the year/month/day. */
@@ -3007,6 +3173,32 @@
 	return (as->s);
 }
 
+static int
+build_pathname_utf16be(unsigned char *p, size_t max, size_t *len,
+    struct file_info *file)
+{
+	if (file->parent != NULL && file->parent->utf16be_bytes > 0) {
+		if (build_pathname_utf16be(p, max, len, file->parent) != 0)
+			return (-1);
+		p[*len] = 0;
+		p[*len + 1] = '/';
+		*len += 2;
+	}
+	if (file->utf16be_bytes == 0) {
+		if (*len + 2 > max)
+			return (-1);/* Path is too long! */
+		p[*len] = 0;
+		p[*len + 1] = '.';
+		*len += 2;
+	} else {
+		if (*len + file->utf16be_bytes > max)
+			return (-1);/* Path is too long! */
+		memcpy(p + *len, file->utf16be_name, file->utf16be_bytes);
+		*len += file->utf16be_bytes;
+	}
+	return (0);
+}
+
 #if DEBUG
 static void
 dump_isodirrec(FILE *out, const unsigned char *isodirrec)
@@ -3019,7 +3211,7 @@
 	    toi(isodirrec + DR_extent_offset, DR_extent_size));
 	fprintf(out, " s %d,",
 	    toi(isodirrec + DR_size_offset, DR_extent_size));
-	fprintf(out, " f 0x%02x,",
+	fprintf(out, " f 0x%x,",
 	    toi(isodirrec + DR_flags_offset, DR_flags_size));
 	fprintf(out, " u %d,",
 	    toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size));
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_support_format_mtree.c
--- a/head/contrib/libarchive/libarchive/archive_read_support_format_mtree.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_support_format_mtree.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
  * Copyright (c) 2008 Joerg Sonnenberger
+ * Copyright (c) 2011 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,7 +26,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_mtree.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_mtree.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -86,9 +87,8 @@
 	struct archive_string	 line;
 	size_t			 buffsize;
 	char			*buff;
-	off_t			 offset;
+	int64_t			 offset;
 	int			 fd;
-	int			 filetype;
 	int			 archive_format;
 	const char		*archive_format_name;
 	struct mtree_entry	*entries;
@@ -98,11 +98,12 @@
 
 	struct archive_entry_linkresolver *resolver;
 
-	off_t			 cur_size, cur_offset;
+	int64_t			 cur_size;
 };
 
+static int	bid_keycmp(const char *, const char *, ssize_t);
 static int	cleanup(struct archive_read *);
-static int	mtree_bid(struct archive_read *);
+static int	mtree_bid(struct archive_read *, int);
 static int	parse_file(struct archive_read *, struct archive_entry *,
 		    struct mtree *, struct mtree_entry *, int *);
 static void	parse_escapes(char *, struct mtree_entry *);
@@ -111,7 +112,7 @@
 static int	parse_keyword(struct archive_read *, struct mtree *,
 		    struct archive_entry *, struct mtree_option *, int *);
 static int	read_data(struct archive_read *a,
-		    const void **buff, size_t *size, off_t *offset);
+		    const void **buff, size_t *size, int64_t *offset);
 static ssize_t	readline(struct archive_read *, struct mtree *, char **, ssize_t);
 static int	skip(struct archive_read *a);
 static int	read_header(struct archive_read *,
@@ -120,6 +121,53 @@
 static int64_t	mtree_atol8(char **);
 static int64_t	mtree_atol(char **);
 
+/*
+ * There's no standard for TIME_T_MAX/TIME_T_MIN.  So we compute them
+ * here.  TODO: Move this to configure time, but be careful
+ * about cross-compile environments.
+ */
+static int64_t
+get_time_t_max(void)
+{
+#if defined(TIME_T_MAX)
+	return TIME_T_MAX;
+#else
+	static time_t t;
+	time_t a;
+	if (t == 0) {
+		a = 1;
+		while (a > t) {
+			t = a;
+			a = a * 2 + 1;
+		}
+	}
+	return t;
+#endif
+}
+
+static int64_t
+get_time_t_min(void)
+{
+#if defined(TIME_T_MIN)
+	return TIME_T_MIN;
+#else
+	/* 't' will hold the minimum value, which will be zero (if
+	 * time_t is unsigned) or -2^n (if time_t is signed). */
+	static int computed;
+	static time_t t;
+	time_t a;
+	if (computed == 0) {
+		a = (time_t)-1;
+		while (a < t) {
+			t = a;
+			a = a * 2;
+		}			
+		computed = 1;
+	}
+	return t;
+#endif
+}
+
 static void
 free_options(struct mtree_option *head)
 {
@@ -139,6 +187,9 @@
 	struct mtree *mtree;
 	int r;
 
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_mtree");
+
 	mtree = (struct mtree *)malloc(sizeof(*mtree));
 	if (mtree == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
@@ -183,20 +234,389 @@
 	return (ARCHIVE_OK);
 }
 
+static ssize_t
+get_line_size(const char *b, ssize_t avail, ssize_t *nlsize)
+{
+	ssize_t len;
+
+	len = 0;
+	while (len < avail) {
+		switch (*b) {
+		case '\0':/* Non-ascii character or control character. */
+			if (nlsize != NULL)
+				*nlsize = 0;
+			return (-1);
+		case '\r':
+			if (avail-len > 1 && b[1] == '\n') {
+				if (nlsize != NULL)
+					*nlsize = 2;
+				return (len+2);
+			}
+			/* FALL THROUGH */
+		case '\n':
+			if (nlsize != NULL)
+				*nlsize = 1;
+			return (len+1);
+		default:
+			b++;
+			len++;
+			break;
+		}
+	}
+	if (nlsize != NULL)
+		*nlsize = 0;
+	return (avail);
+}
+
+static ssize_t
+next_line(struct archive_read *a,
+    const char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl)
+{
+	ssize_t len;
+	int quit;
+	
+	quit = 0;
+	if (*avail == 0) {
+		*nl = 0;
+		len = 0;
+	} else
+		len = get_line_size(*b, *avail, nl);
+	/*
+	 * Read bytes more while it does not reach the end of line.
+	 */
+	while (*nl == 0 && len == *avail && !quit) {
+		ssize_t diff = *ravail - *avail;
+		size_t nbytes_req = (*ravail+1023) & ~1023U;
+		ssize_t tested;
+
+		/* Increase reading bytes if it is not enough to at least
+		 * new two lines. */
+		if (nbytes_req < (size_t)*ravail + 160)
+			nbytes_req <<= 1;
+
+		*b = __archive_read_ahead(a, nbytes_req, avail);
+		if (*b == NULL) {
+			if (*ravail >= *avail)
+				return (0);
+			/* Reading bytes reaches the end of file. */
+			*b = __archive_read_ahead(a, *avail, avail);
+			quit = 1;
+		}
+		*ravail = *avail;
+		*b += diff;
+		*avail -= diff;
+		tested = len;/* Skip some bytes we already determinated. */
+		len = get_line_size(*b, *avail, nl);
+		if (len >= 0)
+			len += tested;
+	}
+	return (len);
+}
+
+/*
+ * Compare characters with a mtree keyword.
+ * Returns the length of a mtree keyword if matched.
+ * Returns 0 if not matched.
+ */
+static int
+bid_keycmp(const char *p, const char *key, ssize_t len)
+{
+	int match_len = 0;
+
+	while (len > 0 && *p && *key) {
+		if (*p == *key) {
+			--len;
+			++p;
+			++key;
+			++match_len;
+			continue;
+		}
+		return (0);/* Not match */
+	}
+	if (*key != '\0')
+		return (0);/* Not match */
+
+	/* A following character should be specified characters */
+	if (p[0] == '=' || p[0] == ' ' || p[0] == '\t' ||
+	    p[0] == '\n' || p[0] == '\r' ||
+	   (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r')))
+		return (match_len);
+	return (0);/* Not match */
+}
+
+/*
+ * Test whether the characters 'p' has is mtree keyword.
+ * Returns the length of a detected keyword.
+ * Returns 0 if any keywords were not found.
+ */
+static ssize_t
+bid_keyword(const char *p,  ssize_t len)
+{
+	static const char *keys_c[] = {
+		"content", "contents", "cksum", NULL
+	};
+	static const char *keys_df[] = {
+		"device", "flags", NULL
+	};
+	static const char *keys_g[] = {
+		"gid", "gname", NULL
+	};
+	static const char *keys_il[] = {
+		"ignore", "link", NULL
+	};
+	static const char *keys_m[] = {
+		"md5", "md5digest", "mode", NULL
+	};
+	static const char *keys_no[] = {
+		"nlink", "optional", NULL
+	};
+	static const char *keys_r[] = {
+		"rmd160", "rmd160digest", NULL
+	};
+	static const char *keys_s[] = {
+		"sha1", "sha1digest",
+		"sha256", "sha256digest",
+		"sha384", "sha384digest",
+		"sha512", "sha512digest",
+		"size", NULL
+	};
+	static const char *keys_t[] = {
+		"tags", "time", "type", NULL
+	};
+	static const char *keys_u[] = {
+		"uid", "uname",	NULL
+	};
+	const char **keys;
+	int i;
+
+	switch (*p) {
+	case 'c': keys = keys_c; break;
+	case 'd': case 'f': keys = keys_df; break;
+	case 'g': keys = keys_g; break;
+	case 'i': case 'l': keys = keys_il; break;
+	case 'm': keys = keys_m; break;
+	case 'n': case 'o': keys = keys_no; break;
+	case 'r': keys = keys_r; break;
+	case 's': keys = keys_s; break;
+	case 't': keys = keys_t; break;
+	case 'u': keys = keys_u; break;
+	default: return (0);/* Unknown key */
+	}
+
+	for (i = 0; keys[i] != NULL; i++) {
+		int l = bid_keycmp(p, keys[i], len);
+		if (l > 0)
+			return (l);
+	}
+	return (0);/* Unknown key */
+}
+
+/*
+ * Test whether there is a set of mtree keywords.
+ * Returns the number of keyword.
+ * Returns -1 if we got incorrect sequence.
+ * This function expects a set of "<space characters>keyword=value".
+ * When "unset" is specified, expects a set of "<space characters>keyword".
+ */
+static int
+bid_keyword_list(const char *p,  ssize_t len, int unset)
+{
+	int l;
+	int keycnt = 0;
+
+	while (len > 0 && *p) {
+		int blank = 0;
+
+		/* Test whether there are blank characters in the line. */
+		while (len >0 && (*p == ' ' || *p == '\t')) {
+			++p;
+			--len;
+			blank = 1;
+		}
+		if (*p == '\n' || *p == '\r')
+			break;
+		if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r'))
+			break;
+		if (!blank) /* No blank character. */
+			return (-1);
+
+		if (unset) {
+			l = bid_keycmp(p, "all", len);
+			if (l > 0)
+				return (1);
+		}
+		/* Test whether there is a correct key in the line. */
+		l = bid_keyword(p, len);
+		if (l == 0)
+			return (-1);/* Unknown keyword was found. */
+		p += l;
+		len -= l;
+		keycnt++;
+
+		/* Skip value */
+		if (*p == '=') {
+			int value = 0;
+			++p;
+			--len;
+			while (len > 0 && *p != ' ' && *p != '\t') {
+				++p;
+				--len;
+				value = 1;
+			}
+			/* A keyword should have a its value unless
+			 * "/unset" operation. */ 
+			if (!unset && value == 0)
+				return (-1);
+		}
+	}
+	return (keycnt);
+}
 
 static int
-mtree_bid(struct archive_read *a)
+bid_entry(const char *p, ssize_t len)
+{
+	int f = 0;
+	static const unsigned char safe_char[256] = {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+		/* !"$%&'()*+,-./  EXCLUSION:( )(#) */
+		0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+		/* 0123456789:;<>?  EXCLUSION:(=) */
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */
+		/* @ABCDEFGHIJKLMNO */
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+		/* PQRSTUVWXYZ[\]^_  */
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
+		/* `abcdefghijklmno */
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+		/* pqrstuvwxyz{|}~ */
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+	};
+
+	/*
+	 * Skip the path-name which is quoted.
+	 */
+	while (len > 0 && *p != ' ' && *p != '\t') {
+		if (!safe_char[*(const unsigned char *)p])
+			return (-1);
+		++p;
+		--len;
+		++f;
+	}
+	/* If a path-name was not found, returns error. */
+	if (f == 0)
+		return (-1);
+
+	return (bid_keyword_list(p, len, 0));
+}
+
+#define MAX_BID_ENTRY	3
+
+static int
+mtree_bid(struct archive_read *a, int best_bid)
 {
 	const char *signature = "#mtree";
 	const char *p;
+	ssize_t avail, ravail;
+	ssize_t len, nl;
+	int detected_bytes = 0, entry_cnt = 0, multiline = 0;
+
+	(void)best_bid; /* UNUSED */
 
 	/* Now let's look at the actual header and see if it matches. */
-	p = __archive_read_ahead(a, strlen(signature), NULL);
+	p = __archive_read_ahead(a, strlen(signature), &avail);
 	if (p == NULL)
 		return (-1);
 
-	if (strncmp(p, signature, strlen(signature)) == 0)
+	if (memcmp(p, signature, strlen(signature)) == 0)
 		return (8 * (int)strlen(signature));
+
+	/*
+	 * There is not a mtree signature. Let's try to detect mtree format.
+	 */
+	ravail = avail;
+	for (;;) {
+		len = next_line(a, &p, &avail, &ravail, &nl);
+		/* The terminal character of the line should be
+		 * a new line character, '\r\n' or '\n'. */
+		if (len <= 0 || nl == 0)
+			break;
+		if (!multiline) {
+			/* Leading whitespace is never significant,
+			 * ignore it. */
+			while (len > 0 && (*p == ' ' || *p == '\t')) {
+				++p;
+				--avail;
+				--len;
+			}
+			/* Skip comment or empty line. */ 
+			if (p[0] == '#' || p[0] == '\n' || p[0] == '\r') {
+				p += len;
+				avail -= len;
+				continue;
+			}
+		} else {
+			/* A continuance line; the terminal
+			 * character of previous line was '\' character. */
+			if (bid_keyword_list(p, len, 0) <= 0)
+				break;
+			if (multiline == 1)
+				detected_bytes += len;
+			if (p[len-nl-1] != '\\') {
+				if (multiline == 1 &&
+				    ++entry_cnt >= MAX_BID_ENTRY)
+					break;
+				multiline = 0;
+			}
+			p += len;
+			avail -= len;
+			continue;
+		}
+		if (p[0] != '/') {
+			if (bid_entry(p, len) >= 0) {
+				detected_bytes += len;
+				if (p[len-nl-1] == '\\')
+					/* This line continues. */
+					multiline = 1;
+				else {
+					/* We've got plenty of correct lines
+					 * to assume that this file is a mtree
+					 * format. */
+					if (++entry_cnt >= MAX_BID_ENTRY)
+						break;
+				}
+			} else
+				break;
+		} else if (strncmp(p, "/set", 4) == 0) {
+			if (bid_keyword_list(p+4, len-4, 0) <= 0)
+				break;
+			/* This line continues. */
+			if (p[len-nl-1] == '\\')
+				multiline = 2;
+		} else if (strncmp(p, "/unset", 6) == 0) {
+			if (bid_keyword_list(p+6, len-6, 1) <= 0)
+				break;
+			/* This line continues. */
+			if (p[len-nl-1] == '\\')
+				multiline = 2;
+		} else
+			break;
+
+		/* Test next line. */
+		p += len;
+		avail -= len;
+	}
+	if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0))
+		return (32);
+
 	return (0);
 }
 
@@ -215,21 +635,21 @@
 add_option(struct archive_read *a, struct mtree_option **global,
     const char *value, size_t len)
 {
-	struct mtree_option *option;
+	struct mtree_option *opt;
 
-	if ((option = malloc(sizeof(*option))) == NULL) {
+	if ((opt = malloc(sizeof(*opt))) == NULL) {
 		archive_set_error(&a->archive, errno, "Can't allocate memory");
 		return (ARCHIVE_FATAL);
 	}
-	if ((option->value = malloc(len + 1)) == NULL) {
-		free(option);
+	if ((opt->value = malloc(len + 1)) == NULL) {
+		free(opt);
 		archive_set_error(&a->archive, errno, "Can't allocate memory");
 		return (ARCHIVE_FATAL);
 	}
-	memcpy(option->value, value, len);
-	option->value[len] = '\0';
-	option->next = *global;
-	*global = option;
+	memcpy(opt->value, value, len);
+	opt->value[len] = '\0';
+	opt->next = *global;
+	*global = opt;
 	return (ARCHIVE_OK);
 }
 
@@ -400,7 +820,7 @@
 	last_entry = NULL;
 
 	for (counter = 1; ; ++counter) {
-		len = readline(a, mtree, &p, 256);
+		len = readline(a, mtree, &p, 65536);
 		if (len == 0) {
 			mtree->this_entry = mtree->entries;
 			free_options(global);
@@ -518,12 +938,12 @@
 	struct stat st_storage, *st;
 	struct mtree_entry *mp;
 	struct archive_entry *sparse_entry;
-	int r = ARCHIVE_OK, r1, parsed_kws, mismatched_type;
+	int r = ARCHIVE_OK, r1, parsed_kws;
 
 	mentry->used = 1;
 
 	/* Initialize reasonable defaults. */
-	mtree->filetype = AE_IFREG;
+	archive_entry_set_filetype(entry, AE_IFREG);
 	archive_entry_set_size(entry, 0);
 	archive_string_empty(&mtree->contents_name);
 
@@ -618,44 +1038,49 @@
 	 * the type of the contents object on disk.
 	 */
 	if (st != NULL) {
-		mismatched_type = 0;
-		if ((st->st_mode & S_IFMT) == S_IFREG &&
-		    archive_entry_filetype(entry) != AE_IFREG)
-			mismatched_type = 1;
-		if ((st->st_mode & S_IFMT) == S_IFLNK &&
-		    archive_entry_filetype(entry) != AE_IFLNK)
-			mismatched_type = 1;
-		if ((st->st_mode & S_IFSOCK) == S_IFSOCK &&
-		    archive_entry_filetype(entry) != AE_IFSOCK)
-			mismatched_type = 1;
-		if ((st->st_mode & S_IFMT) == S_IFCHR &&
-		    archive_entry_filetype(entry) != AE_IFCHR)
-			mismatched_type = 1;
-		if ((st->st_mode & S_IFMT) == S_IFBLK &&
-		    archive_entry_filetype(entry) != AE_IFBLK)
-			mismatched_type = 1;
-		if ((st->st_mode & S_IFMT) == S_IFDIR &&
-		    archive_entry_filetype(entry) != AE_IFDIR)
-			mismatched_type = 1;
-		if ((st->st_mode & S_IFMT) == S_IFIFO &&
-		    archive_entry_filetype(entry) != AE_IFIFO)
-			mismatched_type = 1;
-
-		if (mismatched_type) {
-			if ((parsed_kws & MTREE_HAS_OPTIONAL) == 0) {
+		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;
-			} else {
-				*use_next = 1;
 			}
-			/* Don't hold a non-regular file open. */
-			if (mtree->fd >= 0)
-				close(mtree->fd);
-			mtree->fd = -1;
-			st = NULL;
 			return r;
 		}
 	}
@@ -735,7 +1160,7 @@
 		if (r1 < r)
 			r = r1;
 	}
-	if ((*parsed_kws & MTREE_HAS_TYPE) == 0) {
+	if (r == ARCHIVE_OK && (*parsed_kws & MTREE_HAS_TYPE) == 0) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Missing type keyword in mtree specification");
 		return (ARCHIVE_WARN);
@@ -779,11 +1204,11 @@
  */
 static int
 parse_keyword(struct archive_read *a, struct mtree *mtree,
-    struct archive_entry *entry, struct mtree_option *option, int *parsed_kws)
+    struct archive_entry *entry, struct mtree_option *opt, int *parsed_kws)
 {
 	char *val, *key;
 
-	key = option->value;
+	key = opt->value;
 
 	if (*key == '\0')
 		return (ARCHIVE_OK);
@@ -900,58 +1325,67 @@
 			break;
 		}
 		if (strcmp(key, "time") == 0) {
-			time_t m;
+			int64_t m;
+			int64_t my_time_t_max = get_time_t_max();
+			int64_t my_time_t_min = get_time_t_min();
 			long ns;
 
 			*parsed_kws |= MTREE_HAS_MTIME;
-			m = (time_t)mtree_atol10(&val);
+			m = mtree_atol10(&val);
+			/* Replicate an old mtree bug:
+			 * 123456789.1 represents 123456789
+			 * seconds and 1 nanosecond. */
 			if (*val == '.') {
 				++val;
 				ns = (long)mtree_atol10(&val);
 			} else
 				ns = 0;
-			archive_entry_set_mtime(entry, m, ns);
+			if (m > my_time_t_max)
+				m = my_time_t_max;
+			else if (m < my_time_t_min)
+				m = my_time_t_min;
+			archive_entry_set_mtime(entry, (time_t)m, ns);
 			break;
 		}
 		if (strcmp(key, "type") == 0) {
-			*parsed_kws |= MTREE_HAS_TYPE;
 			switch (val[0]) {
 			case 'b':
 				if (strcmp(val, "block") == 0) {
-					mtree->filetype = AE_IFBLK;
+					archive_entry_set_filetype(entry, AE_IFBLK);
 					break;
 				}
 			case 'c':
 				if (strcmp(val, "char") == 0) {
-					mtree->filetype = AE_IFCHR;
+					archive_entry_set_filetype(entry, AE_IFCHR);
 					break;
 				}
 			case 'd':
 				if (strcmp(val, "dir") == 0) {
-					mtree->filetype = AE_IFDIR;
+					archive_entry_set_filetype(entry, AE_IFDIR);
 					break;
 				}
 			case 'f':
 				if (strcmp(val, "fifo") == 0) {
-					mtree->filetype = AE_IFIFO;
+					archive_entry_set_filetype(entry, AE_IFIFO);
 					break;
 				}
 				if (strcmp(val, "file") == 0) {
-					mtree->filetype = AE_IFREG;
+					archive_entry_set_filetype(entry, AE_IFREG);
 					break;
 				}
 			case 'l':
 				if (strcmp(val, "link") == 0) {
-					mtree->filetype = AE_IFLNK;
+					archive_entry_set_filetype(entry, AE_IFLNK);
 					break;
 				}
 			default:
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Unrecognized file type \"%s\"", val);
+				    "Unrecognized file type \"%s\"; assuming \"file\"", val);
+				archive_entry_set_filetype(entry, AE_IFREG);
 				return (ARCHIVE_WARN);
 			}
-			archive_entry_set_filetype(entry, mtree->filetype);
+			*parsed_kws |= MTREE_HAS_TYPE;
 			break;
 		}
 	case 'u':
@@ -974,7 +1408,7 @@
 }
 
 static int
-read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset)
+read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset)
 {
 	size_t bytes_to_read;
 	ssize_t bytes_read;
@@ -999,7 +1433,7 @@
 
 	*buff = mtree->buff;
 	*offset = mtree->offset;
-	if ((off_t)mtree->buffsize > mtree->cur_size - mtree->offset)
+	if ((int64_t)mtree->buffsize > mtree->cur_size - mtree->offset)
 		bytes_to_read = mtree->cur_size - mtree->offset;
 	else
 		bytes_to_read = mtree->buffsize;
@@ -1147,28 +1581,43 @@
 	int base, digit, sign;
 
 	base = 10;
-	limit = INT64_MAX / base;
-	last_digit_limit = INT64_MAX % base;
 
 	if (**p == '-') {
 		sign = -1;
+		limit = ((uint64_t)(INT64_MAX) + 1) / base;
+		last_digit_limit = ((uint64_t)(INT64_MAX) + 1) % base;
 		++(*p);
-	} else
+	} else {
 		sign = 1;
+		limit = INT64_MAX / base;
+		last_digit_limit = INT64_MAX % base;
+	}
 
 	l = 0;
 	digit = **p - '0';
 	while (digit >= 0 && digit < base) {
-		if (l > limit || (l == limit && digit > last_digit_limit)) {
-			l = INT64_MAX; /* Truncate on overflow. */
-			break;
-		}
+		if (l > limit || (l == limit && digit > last_digit_limit))
+			return (sign < 0) ? INT64_MIN : INT64_MAX;
 		l = (l * base) + digit;
 		digit = *++(*p) - '0';
 	}
 	return (sign < 0) ? -l : l;
 }
 
+/* Parse a hex digit. */
+static int
+parsehex(char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	else if (c >= 'a' && c <= 'f')
+		return c - 'a';
+	else if (c >= 'A' && c <= 'F')
+		return c - 'A';
+	else
+		return -1;
+}
+
 /*
  * Note that this implementation does not (and should not!) obey
  * locale settings; you cannot simply substitute strtol here, since
@@ -1181,38 +1630,25 @@
 	int base, digit, sign;
 
 	base = 16;
-	limit = INT64_MAX / base;
-	last_digit_limit = INT64_MAX % base;
 
 	if (**p == '-') {
 		sign = -1;
+		limit = ((uint64_t)(INT64_MAX) + 1) / base;
+		last_digit_limit = ((uint64_t)(INT64_MAX) + 1) % base;
 		++(*p);
-	} else
+	} else {
 		sign = 1;
+		limit = INT64_MAX / base;
+		last_digit_limit = INT64_MAX % base;
+	}
 
 	l = 0;
-	if (**p >= '0' && **p <= '9')
-		digit = **p - '0';
-	else if (**p >= 'a' && **p <= 'f')
-		digit = **p - 'a' + 10;
-	else if (**p >= 'A' && **p <= 'F')
-		digit = **p - 'A' + 10;
-	else
-		digit = -1;
+	digit = parsehex(**p);
 	while (digit >= 0 && digit < base) {
-		if (l > limit || (l == limit && digit > last_digit_limit)) {
-			l = INT64_MAX; /* Truncate on overflow. */
-			break;
-		}
+		if (l > limit || (l == limit && digit > last_digit_limit))
+			return (sign < 0) ? INT64_MIN : INT64_MAX;
 		l = (l * base) + digit;
-		if (**p >= '0' && **p <= '9')
-			digit = **p - '0';
-		else if (**p >= 'a' && **p <= 'f')
-			digit = **p - 'a' + 10;
-		else if (**p >= 'A' && **p <= 'F')
-			digit = **p - 'A' + 10;
-		else
-			digit = -1;
+		digit = parsehex(*++(*p));
 	}
 	return (sign < 0) ? -l : l;
 }
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_support_format_raw.c
--- a/head/contrib/libarchive/libarchive/archive_read_support_format_raw.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_support_format_raw.c	Fri Mar 02 16:54:40 2012 +0200
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_raw.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_raw.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -40,13 +40,14 @@
 
 struct raw_info {
 	int64_t offset; /* Current position in the file. */
+	int64_t unconsumed;
 	int     end_of_file;
 };
 
-static int	archive_read_format_raw_bid(struct archive_read *);
+static int	archive_read_format_raw_bid(struct archive_read *, int);
 static int	archive_read_format_raw_cleanup(struct archive_read *);
 static int	archive_read_format_raw_read_data(struct archive_read *,
-		    const void **, size_t *, off_t *);
+		    const void **, size_t *, int64_t *);
 static int	archive_read_format_raw_read_data_skip(struct archive_read *);
 static int	archive_read_format_raw_read_header(struct archive_read *,
 		    struct archive_entry *);
@@ -58,6 +59,9 @@
 	struct archive_read *a = (struct archive_read *)_a;
 	int r;
 
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_raw");
+
 	info = (struct raw_info *)calloc(1, sizeof(*info));
 	if (info == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
@@ -87,12 +91,11 @@
  * include "raw" as part of support_format_all().
  */
 static int
-archive_read_format_raw_bid(struct archive_read *a)
+archive_read_format_raw_bid(struct archive_read *a, int best_bid)
 {
-
-	if (__archive_read_ahead(a, 1, NULL) == NULL)
-		return (-1);
-	return (1);
+	if (best_bid < 1 && __archive_read_ahead(a, 1, NULL) != NULL)
+		return (1);
+	return (-1);
 }
 
 /*
@@ -109,32 +112,40 @@
 		return (ARCHIVE_EOF);
 
 	a->archive.archive_format = ARCHIVE_FORMAT_RAW;
-	a->archive.archive_format_name = "Raw data";
+	a->archive.archive_format_name = "raw";
 	archive_entry_set_pathname(entry, "data");
-	/* XXX should we set mode to mimic a regular file? XXX */
+	archive_entry_set_filetype(entry, AE_IFREG);
+	archive_entry_set_perm(entry, 0644);
 	/* I'm deliberately leaving most fields unset here. */
 	return (ARCHIVE_OK);
 }
 
 static int
 archive_read_format_raw_read_data(struct archive_read *a,
-    const void **buff, size_t *size, off_t *offset)
+    const void **buff, size_t *size, int64_t *offset)
 {
 	struct raw_info *info;
 	ssize_t avail;
 
 	info = (struct raw_info *)(a->format->data);
+
+	/* Consume the bytes we read last time. */
+	if (info->unconsumed) {
+		__archive_read_consume(a, info->unconsumed);
+		info->unconsumed = 0;
+	}
+
 	if (info->end_of_file)
 		return (ARCHIVE_EOF);
 
 	/* Get whatever bytes are immediately available. */
 	*buff = __archive_read_ahead(a, 1, &avail);
 	if (avail > 0) {
-		/* Consume and return the bytes we just read */
-		__archive_read_consume(a, avail);
+		/* Return the bytes we just read */
 		*size = avail;
 		*offset = info->offset;
 		info->offset += *size;
+		info->unconsumed = avail;
 		return (ARCHIVE_OK);
 	} else if (0 == avail) {
 		/* Record and return end-of-file. */
@@ -153,24 +164,15 @@
 static int
 archive_read_format_raw_read_data_skip(struct archive_read *a)
 {
-	struct raw_info *info;
-	off_t bytes_skipped;
-	int64_t request = 1024 * 1024 * 1024UL; /* Skip 1 GB at a time. */
+	struct raw_info *info = (struct raw_info *)(a->format->data);
 
-	info = (struct raw_info *)(a->format->data);
-	if (info->end_of_file)
-		return (ARCHIVE_EOF);
+	/* Consume the bytes we read last time. */
+	if (info->unconsumed) {
+		__archive_read_consume(a, info->unconsumed);
+		info->unconsumed = 0;
+	}
 	info->end_of_file = 1;
-
-	for (;;) {
-		bytes_skipped = __archive_read_skip_lenient(a, request);
-		if (bytes_skipped < 0)
-			return (ARCHIVE_FATAL);
-		if (bytes_skipped < request)
-			return (ARCHIVE_OK);
-		/* We skipped all the bytes we asked for.  There might
-		 * be more, so try again. */
-	}
+	return (ARCHIVE_OK);
 }
 
 static int
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_support_format_tar.c
--- a/head/contrib/libarchive/libarchive/archive_read_support_format_tar.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_support_format_tar.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,13 +25,12 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_tar.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_tar.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
 #include <stddef.h>
-/* #include <stdint.h> */ /* See archive_platform.h */
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
@@ -38,37 +38,10 @@
 #include <string.h>
 #endif
 
-/* Obtain suitable wide-character manipulation functions. */
-#ifdef HAVE_WCHAR_H
-#include <wchar.h>
-#else
-/* Good enough for equality testing, which is all we need. */
-static int wcscmp(const wchar_t *s1, const wchar_t *s2)
-{
-	int diff = *s1 - *s2;
-	while (*s1 && diff == 0)
-		diff = (int)*++s1 - (int)*++s2;
-	return diff;
-}
-/* Good enough for equality testing, which is all we need. */
-static int wcsncmp(const wchar_t *s1, const wchar_t *s2, size_t n)
-{
-	int diff = *s1 - *s2;
-	while (*s1 && diff == 0 && n-- > 0)
-		diff = (int)*++s1 - (int)*++s2;
-	return diff;
-}
-static size_t wcslen(const wchar_t *s)
-{
-	const wchar_t *p = s;
-	while (*p)
-		p++;
-	return p - s;
-}
-#endif
-
 #include "archive.h"
+#include "archive_acl_private.h" /* For ACL parsing routines. */
 #include "archive_entry.h"
+#include "archive_entry_locale.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
 
@@ -138,8 +111,9 @@
  */
 struct sparse_block {
 	struct sparse_block	*next;
-	off_t	offset;
-	off_t	remaining;
+	int64_t	offset;
+	int64_t	remaining;
+	int hole;
 };
 
 struct tar {
@@ -156,12 +130,11 @@
 	struct archive_string	 pax_global;
 	struct archive_string	 line;
 	int			 pax_hdrcharset_binary;
-	wchar_t 		*pax_entry;
-	size_t			 pax_entry_length;
 	int			 header_recursion_depth;
 	int64_t			 entry_bytes_remaining;
 	int64_t			 entry_offset;
 	int64_t			 entry_padding;
+	int64_t 		 entry_bytes_unconsumed;
 	int64_t			 realsize;
 	struct sparse_block	*sparse_list;
 	struct sparse_block	*sparse_last;
@@ -170,70 +143,89 @@
 	int			 sparse_gnu_major;
 	int			 sparse_gnu_minor;
 	char			 sparse_gnu_pending;
+
+	struct archive_string	 localname;
+	struct archive_string_conv *opt_sconv;
+	struct archive_string_conv *sconv;
+	struct archive_string_conv *sconv_acl;
+	struct archive_string_conv *sconv_default;
+	int			 init_default_conversion;
+	int			 compat_2x;
 };
 
-static ssize_t	UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n);
-static int	archive_block_is_null(const unsigned char *p);
+static int	archive_block_is_null(const char *p);
 static char	*base64_decode(const char *, size_t, size_t *);
-static void	 gnu_add_sparse_entry(struct tar *,
-		    off_t offset, off_t remaining);
+static int	gnu_add_sparse_entry(struct archive_read *, struct tar *,
+		    int64_t offset, int64_t remaining);
+
 static void	gnu_clear_sparse_list(struct tar *);
 static int	gnu_sparse_old_read(struct archive_read *, struct tar *,
-		    const struct archive_entry_header_gnutar *header);
-static void	gnu_sparse_old_parse(struct tar *,
+		    const struct archive_entry_header_gnutar *header, size_t *);
+static int	gnu_sparse_old_parse(struct archive_read *, struct tar *,
 		    const struct gnu_sparse *sparse, int length);
-static int	gnu_sparse_01_parse(struct tar *, const char *);
-static ssize_t	gnu_sparse_10_read(struct archive_read *, struct tar *);
+static int	gnu_sparse_01_parse(struct archive_read *, struct tar *,
+		    const char *);
+static ssize_t	gnu_sparse_10_read(struct archive_read *, struct tar *,
+			size_t *);
 static int	header_Solaris_ACL(struct archive_read *,  struct tar *,
-		    struct archive_entry *, const void *);
+		    struct archive_entry *, const void *, size_t *);
 static int	header_common(struct archive_read *,  struct tar *,
 		    struct archive_entry *, const void *);
 static int	header_old_tar(struct archive_read *, struct tar *,
 		    struct archive_entry *, const void *);
 static int	header_pax_extensions(struct archive_read *, struct tar *,
-		    struct archive_entry *, const void *);
+		    struct archive_entry *, const void *, size_t *);
 static int	header_pax_global(struct archive_read *, struct tar *,
-		    struct archive_entry *, const void *h);
+		    struct archive_entry *, const void *h, size_t *);
 static int	header_longlink(struct archive_read *, struct tar *,
-		    struct archive_entry *, const void *h);
+		    struct archive_entry *, const void *h, size_t *);
 static int	header_longname(struct archive_read *, struct tar *,
-		    struct archive_entry *, const void *h);
+		    struct archive_entry *, const void *h, size_t *);
+static int	read_mac_metadata_blob(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h, size_t *);
 static int	header_volume(struct archive_read *, struct tar *,
-		    struct archive_entry *, const void *h);
+		    struct archive_entry *, const void *h, size_t *);
 static int	header_ustar(struct archive_read *, struct tar *,
 		    struct archive_entry *, const void *h);
 static int	header_gnutar(struct archive_read *, struct tar *,
-		    struct archive_entry *, const void *h);
-static int	archive_read_format_tar_bid(struct archive_read *);
+		    struct archive_entry *, const void *h, size_t *);
+static int	archive_read_format_tar_bid(struct archive_read *, int);
+static int	archive_read_format_tar_options(struct archive_read *,
+		    const char *, const char *);
 static int	archive_read_format_tar_cleanup(struct archive_read *);
 static int	archive_read_format_tar_read_data(struct archive_read *a,
-		    const void **buff, size_t *size, off_t *offset);
+		    const void **buff, size_t *size, int64_t *offset);
 static int	archive_read_format_tar_skip(struct archive_read *a);
 static int	archive_read_format_tar_read_header(struct archive_read *,
 		    struct archive_entry *);
 static int	checksum(struct archive_read *, const void *);
-static int 	pax_attribute(struct tar *, struct archive_entry *,
-		    char *key, char *value);
+static int 	pax_attribute(struct archive_read *, struct tar *,
+		    struct archive_entry *, char *key, char *value);
 static int 	pax_header(struct archive_read *, struct tar *,
 		    struct archive_entry *, char *attr);
 static void	pax_time(const char *, int64_t *sec, long *nanos);
 static ssize_t	readline(struct archive_read *, struct tar *, const char **,
-		    ssize_t limit);
+		    ssize_t limit, size_t *);
 static int	read_body_to_string(struct archive_read *, struct tar *,
-		    struct archive_string *, const void *h);
+		    struct archive_string *, const void *h, size_t *);
+static int	solaris_sparse_parse(struct archive_read *, struct tar *,
+		    struct archive_entry *, const char *);
 static int64_t	tar_atol(const char *, unsigned);
 static int64_t	tar_atol10(const char *, unsigned);
 static int64_t	tar_atol256(const char *, unsigned);
 static int64_t	tar_atol8(const char *, unsigned);
 static int	tar_read_header(struct archive_read *, struct tar *,
-		    struct archive_entry *);
+		    struct archive_entry *, size_t *);
 static int	tohex(int c);
 static char	*url_decode(const char *);
-static wchar_t	*utf8_decode(struct tar *, const char *, size_t length);
+static void	tar_flush_unconsumed(struct archive_read *, size_t *);
+
 
 int
 archive_read_support_format_gnutar(struct archive *a)
 {
+	archive_check_magic(a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_gnutar");
 	return (archive_read_support_format_tar(a));
 }
 
@@ -245,17 +237,19 @@
 	struct tar *tar;
 	int r;
 
-	tar = (struct tar *)malloc(sizeof(*tar));
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_tar");
+
+	tar = (struct tar *)calloc(1, sizeof(*tar));
 	if (tar == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate tar data");
 		return (ARCHIVE_FATAL);
 	}
-	memset(tar, 0, sizeof(*tar));
 
 	r = __archive_read_register_format(a, tar, "tar",
 	    archive_read_format_tar_bid,
-	    NULL,
+	    archive_read_format_tar_options,
 	    archive_read_format_tar_read_header,
 	    archive_read_format_tar_read_data,
 	    archive_read_format_tar_skip,
@@ -284,7 +278,7 @@
 	archive_string_free(&tar->pax_header);
 	archive_string_free(&tar->longname);
 	archive_string_free(&tar->longlink);
-	free(tar->pax_entry);
+	archive_string_free(&tar->localname);
 	free(tar);
 	(a->format->data) = NULL;
 	return (ARCHIVE_OK);
@@ -292,12 +286,14 @@
 
 
 static int
-archive_read_format_tar_bid(struct archive_read *a)
+archive_read_format_tar_bid(struct archive_read *a, int best_bid)
 {
 	int bid;
-	const void *h;
+	const char *h;
 	const struct archive_entry_header_ustar *header;
 
+	(void)best_bid; /* UNUSED */
+
 	bid = 0;
 
 	/* Now let's look at the actual header and see if it matches. */
@@ -306,8 +302,7 @@
 		return (-1);
 
 	/* If it's an end-of-archive mark, we can handle it. */
-	if ((*(const char *)h) == 0
-	    && archive_block_is_null((const unsigned char *)h)) {
+	if (h[0] == 0 && archive_block_is_null(h)) {
 		/*
 		 * Usually, I bid the number of bits verified, but
 		 * in this case, 4096 seems excessive so I picked 10 as
@@ -325,12 +320,12 @@
 
 	/* Recognize POSIX formats. */
 	if ((memcmp(header->magic, "ustar\0", 6) == 0)
-	    &&(memcmp(header->version, "00", 2)==0))
+	    && (memcmp(header->version, "00", 2) == 0))
 		bid += 56;
 
 	/* Recognize GNU tar format. */
 	if ((memcmp(header->magic, "ustar ", 6) == 0)
-	    &&(memcmp(header->version, " \0", 2)==0))
+	    && (memcmp(header->version, " \0", 2) == 0))
 		bid += 56;
 
 	/* Type flag must be null, digit or A-Z, a-z. */
@@ -362,8 +357,65 @@
 	return (bid);
 }
 
+static int
+archive_read_format_tar_options(struct archive_read *a,
+    const char *key, const char *val)
+{
+	struct tar *tar;
+	int ret = ARCHIVE_FAILED;
+
+	tar = (struct tar *)(a->format->data);
+	if (strcmp(key, "compat-2x")  == 0) {
+		/* Handle UTF-8 filnames as libarchive 2.x */
+		tar->compat_2x = (val != NULL)?1:0;
+		tar->init_default_conversion = tar->compat_2x;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "tar: hdrcharset option needs a character-set name");
+		else {
+			tar->opt_sconv =
+			    archive_string_conversion_from_charset(
+				&a->archive, val, 0);
+			if (tar->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+/* utility function- this exists to centralize the logic of tracking
+ * how much unconsumed data we have floating around, and to consume
+ * anything outstanding since we're going to do read_aheads
+ */
+static void 
+tar_flush_unconsumed(struct archive_read *a, size_t *unconsumed)
+{
+	if (*unconsumed) {
 /*
- * The function invoked by archive_read_header().  This
+		void *data = (void *)__archive_read_ahead(a, *unconsumed, NULL);
+		 * this block of code is to poison claimed unconsumed space, ensuring
+		 * things break if it is in use still.
+		 * currently it WILL break things, so enable it only for debugging this issue
+		if (data) {
+			memset(data, 0xff, *unconsumed);
+		}
+*/
+		__archive_read_consume(a, *unconsumed);
+		*unconsumed = 0;
+	}
+}
+
+/*
+ * The function invoked by archive_read_next_header().  This
  * just sets up a few things and then calls the internal
  * tar_read_header() function below.
  */
@@ -388,10 +440,9 @@
 	static int default_inode;
 	static int default_dev;
 	struct tar *tar;
-	struct sparse_block *sp;
 	const char *p;
 	int r;
-	size_t l;
+	size_t l, unconsumed = 0;
 
 	/* Assign default device/inode values. */
 	archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */
@@ -404,22 +455,41 @@
 
 	tar = (struct tar *)(a->format->data);
 	tar->entry_offset = 0;
-	while (tar->sparse_list != NULL) {
-		sp = tar->sparse_list;
-		tar->sparse_list = sp->next;
-		free(sp);
-	}
-	tar->sparse_last = NULL;
+	gnu_clear_sparse_list(tar);
 	tar->realsize = -1; /* Mark this as "unset" */
 
-	r = tar_read_header(a, tar, entry);
+	/* Setup default string conversion. */
+	tar->sconv = tar->opt_sconv;
+	if (tar->sconv == NULL) {
+		if (!tar->init_default_conversion) {
+			tar->sconv_default =
+			    archive_string_default_conversion_for_read(&(a->archive));
+			tar->init_default_conversion = 1;
+		}
+		tar->sconv = tar->sconv_default;
+	}
+
+	r = tar_read_header(a, tar, entry, &unconsumed);
+
+	tar_flush_unconsumed(a, &unconsumed);
 
 	/*
 	 * "non-sparse" files are really just sparse files with
 	 * a single block.
 	 */
-	if (tar->sparse_list == NULL)
-		gnu_add_sparse_entry(tar, 0, tar->entry_bytes_remaining);
+	if (tar->sparse_list == NULL) {
+		if (gnu_add_sparse_entry(a, tar, 0, tar->entry_bytes_remaining)
+		    != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	} else {
+		struct sparse_block *sb;
+
+		for (sb = tar->sparse_list; sb != NULL; sb = sb->next) {
+			if (!sb->hole)
+				archive_entry_sparse_add_entry(entry,
+				    sb->offset, sb->remaining);
+		}
+	}
 
 	if (r == ARCHIVE_OK) {
 		/*
@@ -427,18 +497,29 @@
 		 * directory: This is needed for certain old tar
 		 * variants and even for some broken newer ones.
 		 */
-		p = archive_entry_pathname(entry);
-		l = strlen(p);
-		if (archive_entry_filetype(entry) == AE_IFREG
-		    && p[l-1] == '/')
-			archive_entry_set_filetype(entry, AE_IFDIR);
+		const wchar_t *wp;
+		wp = archive_entry_pathname_w(entry);
+		if (wp != NULL) {
+			l = wcslen(wp);
+			if (archive_entry_filetype(entry) == AE_IFREG
+			    && wp[l-1] == L'/')
+				archive_entry_set_filetype(entry, AE_IFDIR);
+		} else {
+			p = archive_entry_pathname(entry);
+			if (p == NULL)
+				return (ARCHIVE_FAILED);
+			l = strlen(p);
+			if (archive_entry_filetype(entry) == AE_IFREG
+			    && p[l-1] == '/')
+				archive_entry_set_filetype(entry, AE_IFDIR);
+		}
 	}
 	return (r);
 }
 
 static int
 archive_read_format_tar_read_data(struct archive_read *a,
-    const void **buff, size_t *size, off_t *offset)
+    const void **buff, size_t *size, int64_t *offset)
 {
 	ssize_t bytes_read;
 	struct tar *tar;
@@ -446,24 +527,7 @@
 
 	tar = (struct tar *)(a->format->data);
 
-	if (tar->sparse_gnu_pending) {
-		if (tar->sparse_gnu_major == 1 && tar->sparse_gnu_minor == 0) {
-			tar->sparse_gnu_pending = 0;
-			/* Read initial sparse map. */
-			bytes_read = gnu_sparse_10_read(a, tar);
-			tar->entry_bytes_remaining -= bytes_read;
-			if (bytes_read < 0)
-				return (bytes_read);
-		} else {
-			*size = 0;
-			*offset = 0;
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-			    "Unrecognized GNU sparse file format");
-			return (ARCHIVE_WARN);
-		}
-		tar->sparse_gnu_pending = 0;
-	}
-
+skip_hole:
 	/* Remove exhausted entries from sparse list. */
 	while (tar->sparse_list != NULL &&
 	    tar->sparse_list->remaining == 0) {
@@ -472,9 +536,14 @@
 		free(p);
 	}
 
+	if (tar->entry_bytes_unconsumed) {
+		__archive_read_consume(a, tar->entry_bytes_unconsumed);
+		tar->entry_bytes_unconsumed = 0;
+	}
+
 	/* If we're at end of file, return EOF. */
 	if (tar->sparse_list == NULL || tar->entry_bytes_remaining == 0) {
-		if (__archive_read_skip(a, tar->entry_padding) < 0)
+		if (__archive_read_consume(a, tar->entry_padding) < 0)
 			return (ARCHIVE_FATAL);
 		tar->entry_padding = 0;
 		*buff = NULL;
@@ -502,7 +571,11 @@
 	tar->sparse_list->remaining -= bytes_read;
 	tar->sparse_list->offset += bytes_read;
 	tar->entry_bytes_remaining -= bytes_read;
-	__archive_read_consume(a, bytes_read);
+	tar->entry_bytes_unconsumed = bytes_read;
+
+	if (tar->sparse_list->hole)
+		goto skip_hole;
+
 	return (ARCHIVE_OK);
 }
 
@@ -514,17 +587,14 @@
 
 	tar = (struct tar *)(a->format->data);
 
-	/*
-	 * Compression layer skip functions are required to either skip the
-	 * length requested or fail, so we can rely upon the entire entry
-	 * plus padding being skipped.
-	 */
-	bytes_skipped = __archive_read_skip(a,
-	    tar->entry_bytes_remaining + tar->entry_padding);
+	bytes_skipped = __archive_read_consume(a,
+	    tar->entry_bytes_remaining + tar->entry_padding + 
+	    tar->entry_bytes_unconsumed);
 	if (bytes_skipped < 0)
 		return (ARCHIVE_FATAL);
 
 	tar->entry_bytes_remaining = 0;
+	tar->entry_bytes_unconsumed = 0;
 	tar->entry_padding = 0;
 
 	/* Free the sparse list. */
@@ -539,40 +609,34 @@
  */
 static int
 tar_read_header(struct archive_read *a, struct tar *tar,
-    struct archive_entry *entry)
+    struct archive_entry *entry, size_t *unconsumed)
 {
 	ssize_t bytes;
 	int err;
-	const void *h;
+	const char *h;
 	const struct archive_entry_header_ustar *header;
 
+	tar_flush_unconsumed(a, unconsumed);
+
 	/* Read 512-byte header record */
 	h = __archive_read_ahead(a, 512, &bytes);
 	if (bytes < 0)
 		return (bytes);
-	if (bytes < 512) {  /* Short read or EOF. */
-		/* Try requesting just one byte and see what happens. */
-		(void)__archive_read_ahead(a, 1, &bytes);
-		if (bytes == 0) {
-			/*
-			 * The archive ends at a 512-byte boundary but
-			 * without a proper end-of-archive marker.
-			 * Yes, there are tar writers that do this;
-			 * hold our nose and accept it.
-			 */
-			return (ARCHIVE_EOF);
-		}
-		/* Archive ends with a partial block; this is bad. */
+	if (bytes == 0) { /* EOF at a block boundary. */
+		/* Some writers do omit the block of nulls. <sigh> */
+		return (ARCHIVE_EOF);
+	}
+	if (bytes < 512) {  /* Short block at EOF; this is bad. */
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated tar archive");
 		return (ARCHIVE_FATAL);
 	}
-	__archive_read_consume(a, 512);
-
+	*unconsumed = 512;
 
 	/* Check for end-of-archive mark. */
-	if (((*(const char *)h)==0) && archive_block_is_null((const unsigned char *)h)) {
+	if (h[0] == 0 && archive_block_is_null(h)) {
 		/* Try to consume a second all-null record, as well. */
+		tar_flush_unconsumed(a, unconsumed);
 		h = __archive_read_ahead(a, 512, NULL);
 		if (h != NULL)
 			__archive_read_consume(a, 512);
@@ -592,53 +656,56 @@
 	 * TODO: Improve this by implementing a real header scan.
 	 */
 	if (!checksum(a, h)) {
+		tar_flush_unconsumed(a, unconsumed);
 		archive_set_error(&a->archive, EINVAL, "Damaged tar archive");
 		return (ARCHIVE_RETRY); /* Retryable: Invalid header */
 	}
 
 	if (++tar->header_recursion_depth > 32) {
+		tar_flush_unconsumed(a, unconsumed);
 		archive_set_error(&a->archive, EINVAL, "Too many special headers");
 		return (ARCHIVE_WARN);
 	}
 
 	/* Determine the format variant. */
 	header = (const struct archive_entry_header_ustar *)h;
+
 	switch(header->typeflag[0]) {
 	case 'A': /* Solaris tar ACL */
 		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
 		a->archive.archive_format_name = "Solaris tar";
-		err = header_Solaris_ACL(a, tar, entry, h);
+		err = header_Solaris_ACL(a, tar, entry, h, unconsumed);
 		break;
 	case 'g': /* POSIX-standard 'g' header. */
 		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
 		a->archive.archive_format_name = "POSIX pax interchange format";
-		err = header_pax_global(a, tar, entry, h);
+		err = header_pax_global(a, tar, entry, h, unconsumed);
 		break;
 	case 'K': /* Long link name (GNU tar, others) */
-		err = header_longlink(a, tar, entry, h);
+		err = header_longlink(a, tar, entry, h, unconsumed);
 		break;
 	case 'L': /* Long filename (GNU tar, others) */
-		err = header_longname(a, tar, entry, h);
+		err = header_longname(a, tar, entry, h, unconsumed);
 		break;
 	case 'V': /* GNU volume header */
-		err = header_volume(a, tar, entry, h);
+		err = header_volume(a, tar, entry, h, unconsumed);
 		break;
 	case 'X': /* Used by SUN tar; same as 'x'. */
 		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
 		a->archive.archive_format_name =
 		    "POSIX pax interchange format (Sun variant)";
-		err = header_pax_extensions(a, tar, entry, h);
+		err = header_pax_extensions(a, tar, entry, h, unconsumed);
 		break;
 	case 'x': /* POSIX-standard 'x' header. */
 		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
 		a->archive.archive_format_name = "POSIX pax interchange format";
-		err = header_pax_extensions(a, tar, entry, h);
+		err = header_pax_extensions(a, tar, entry, h, unconsumed);
 		break;
 	default:
 		if (memcmp(header->magic, "ustar  \0", 8) == 0) {
 			a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
 			a->archive.archive_format_name = "GNU tar format";
-			err = header_gnutar(a, tar, entry, h);
+			err = header_gnutar(a, tar, entry, h, unconsumed);
 		} else if (memcmp(header->magic, "ustar", 5) == 0) {
 			if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {
 				a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
@@ -651,10 +718,50 @@
 			err = header_old_tar(a, tar, entry, h);
 		}
 	}
+	if (err == ARCHIVE_FATAL)
+		return (err);
+
+	tar_flush_unconsumed(a, unconsumed);
+
+	h = NULL;
+	header = NULL;
+
 	--tar->header_recursion_depth;
+	/* Yuck.  Apple's design here ends up storing long pathname
+	 * extensions for both the AppleDouble extension entry and the
+	 * regular entry.
+	 */
+	/* TODO: Should this be disabled on non-Mac platforms? */
+	if ((err == ARCHIVE_WARN || err == ARCHIVE_OK) &&
+	    tar->header_recursion_depth == 0) {
+		int err2 = read_mac_metadata_blob(a, tar, entry, h, unconsumed);
+		if (err2 < err)
+			err = err2;
+	}
+
 	/* We return warnings or success as-is.  Anything else is fatal. */
-	if (err == ARCHIVE_WARN || err == ARCHIVE_OK)
+	if (err == ARCHIVE_WARN || err == ARCHIVE_OK) {
+		if (tar->sparse_gnu_pending) {
+			if (tar->sparse_gnu_major == 1 &&
+			    tar->sparse_gnu_minor == 0) {
+				ssize_t bytes_read;
+
+				tar->sparse_gnu_pending = 0;
+				/* Read initial sparse map. */
+				bytes_read = gnu_sparse_10_read(a, tar, unconsumed);
+				tar->entry_bytes_remaining -= bytes_read;
+				if (bytes_read < 0)
+					return (bytes_read);
+			} else {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Unrecognized GNU sparse file format");
+				return (ARCHIVE_WARN);
+			}
+			tar->sparse_gnu_pending = 0;
+		}
 		return (err);
+	}
 	if (err == ARCHIVE_EOF)
 		/* EOF when recursively reading a header is bad. */
 		archive_set_error(&a->archive, EINVAL, "Damaged tar archive");
@@ -712,7 +819,7 @@
  * Return true if this block contains only nulls.
  */
 static int
-archive_block_is_null(const unsigned char *p)
+archive_block_is_null(const char *p)
 {
 	unsigned i;
 
@@ -727,14 +834,13 @@
  */
 static int
 header_Solaris_ACL(struct archive_read *a, struct tar *tar,
-    struct archive_entry *entry, const void *h)
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
 {
 	const struct archive_entry_header_ustar *header;
 	size_t size;
 	int err;
 	int64_t type;
 	char *acl, *p;
-	wchar_t *wp;
 
 	/*
 	 * read_body_to_string adds a NUL terminator, but we need a little
@@ -742,11 +848,12 @@
 	 */
 	header = (const struct archive_entry_header_ustar *)h;
 	size = tar_atol(header->size, sizeof(header->size));
-	err = read_body_to_string(a, tar, &(tar->acl_text), h);
+	err = read_body_to_string(a, tar, &(tar->acl_text), h, unconsumed);
 	if (err != ARCHIVE_OK)
 		return (err);
+
 	/* Recursively read next header */
-	err = tar_read_header(a, tar, entry);
+	err = tar_read_header(a, tar, entry, unconsumed);
 	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
 		return (err);
 
@@ -802,12 +909,23 @@
 	while (*p != '\0' && p < acl + size)
 		p++;
 
-	wp = utf8_decode(tar, acl, p - acl);
-	err = __archive_entry_acl_parse_w(entry, wp,
-	    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
-	if (err != ARCHIVE_OK)
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Malformed Solaris ACL attribute (unparsable)");
+	if (tar->sconv_acl == NULL) {
+		tar->sconv_acl = archive_string_conversion_from_charset(
+		    &(a->archive), "UTF-8", 1);
+		if (tar->sconv_acl == NULL)
+			return (ARCHIVE_FATAL);
+	}
+	archive_strncpy(&(tar->localname), acl, p - acl);
+	err = archive_acl_parse_l(archive_entry_acl(entry),
+	    tar->localname.s, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, tar->sconv_acl);
+	if (err != ARCHIVE_OK) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for ACL");
+		} else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Malformed Solaris ACL attribute (unparsable)");
+	}
 	return (err);
 }
 
@@ -816,14 +934,14 @@
  */
 static int
 header_longlink(struct archive_read *a, struct tar *tar,
-    struct archive_entry *entry, const void *h)
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
 {
 	int err;
 
-	err = read_body_to_string(a, tar, &(tar->longlink), h);
+	err = read_body_to_string(a, tar, &(tar->longlink), h, unconsumed);
 	if (err != ARCHIVE_OK)
 		return (err);
-	err = tar_read_header(a, tar, entry);
+	err = tar_read_header(a, tar, entry, unconsumed);
 	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
 		return (err);
 	/* Set symlink if symlink already set, else hardlink. */
@@ -831,24 +949,41 @@
 	return (ARCHIVE_OK);
 }
 
+static int
+set_conversion_failed_error(struct archive_read *a,
+    struct archive_string_conv *sconv, const char *name)
+{
+	if (errno == ENOMEM) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for %s", name);
+		return (ARCHIVE_FATAL);
+	}
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "%s can't be converted from %s to current locale.",
+	    name, archive_string_conversion_charset_name(sconv));
+	return (ARCHIVE_WARN);
+}
+
 /*
  * Interpret 'L' long filename header.
  */
 static int
 header_longname(struct archive_read *a, struct tar *tar,
-    struct archive_entry *entry, const void *h)
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
 {
 	int err;
 
-	err = read_body_to_string(a, tar, &(tar->longname), h);
+	err = read_body_to_string(a, tar, &(tar->longname), h, unconsumed);
 	if (err != ARCHIVE_OK)
 		return (err);
 	/* Read and parse "real" header, then override name. */
-	err = tar_read_header(a, tar, entry);
+	err = tar_read_header(a, tar, entry, unconsumed);
 	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
 		return (err);
-	archive_entry_copy_pathname(entry, tar->longname.s);
-	return (ARCHIVE_OK);
+	if (archive_entry_copy_pathname_l(entry, tar->longname.s,
+	    archive_strlen(&(tar->longname)), tar->sconv) != 0)
+		err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+	return (err);
 }
 
 
@@ -857,12 +992,12 @@
  */
 static int
 header_volume(struct archive_read *a, struct tar *tar,
-    struct archive_entry *entry, const void *h)
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
 {
 	(void)h;
 
 	/* Just skip this and read the next header. */
-	return (tar_read_header(a, tar, entry));
+	return (tar_read_header(a, tar, entry, unconsumed));
 }
 
 /*
@@ -870,9 +1005,9 @@
  */
 static int
 read_body_to_string(struct archive_read *a, struct tar *tar,
-    struct archive_string *as, const void *h)
+    struct archive_string *as, const void *h, size_t *unconsumed)
 {
-	off_t size, padded_size;
+	int64_t size;
 	const struct archive_entry_header_ustar *header;
 	const void *src;
 
@@ -892,14 +1027,18 @@
 		return (ARCHIVE_FATAL);
 	}
 
- 	/* Read the body into the string. */
-	padded_size = (size + 511) & ~ 511;
-	src = __archive_read_ahead(a, padded_size, NULL);
-	if (src == NULL)
+	tar_flush_unconsumed(a, unconsumed);
+
+	/* Read the body into the string. */
+	*unconsumed = (size + 511) & ~ 511;
+	src = __archive_read_ahead(a, *unconsumed, NULL);
+	if (src == NULL) {
+		*unconsumed = 0;
 		return (ARCHIVE_FATAL);
+	}
 	memcpy(as->s, src, size);
-	__archive_read_consume(a, padded_size);
 	as->s[size] = '\0';
+	as->length = size;
 	return (ARCHIVE_OK);
 }
 
@@ -919,13 +1058,12 @@
 {
 	const struct archive_entry_header_ustar	*header;
 	char	tartype;
-
-	(void)a; /* UNUSED */
+	int     err = ARCHIVE_OK;
 
 	header = (const struct archive_entry_header_ustar *)h;
 	if (header->linkname[0])
-		archive_strncpy(&(tar->entry_linkpath), header->linkname,
-		    sizeof(header->linkname));
+		archive_strncpy(&(tar->entry_linkpath),
+		    header->linkname, sizeof(header->linkname));
 	else
 		archive_string_empty(&(tar->entry_linkpath));
 
@@ -934,6 +1072,12 @@
 	archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid)));
 	archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid)));
 	tar->entry_bytes_remaining = tar_atol(header->size, sizeof(header->size));
+	if (tar->entry_bytes_remaining < 0) {
+		tar->entry_bytes_remaining = 0;
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Tar entry has negative size?");
+		err = ARCHIVE_WARN;
+	}
 	tar->realsize = tar->entry_bytes_remaining;
 	archive_entry_set_size(entry, tar->entry_bytes_remaining);
 	archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0);
@@ -943,7 +1087,13 @@
 
 	switch (tartype) {
 	case '1': /* Hard link */
-		archive_entry_copy_hardlink(entry, tar->entry_linkpath.s);
+		if (archive_entry_copy_hardlink_l(entry, tar->entry_linkpath.s,
+		    archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) {
+			err = set_conversion_failed_error(a, tar->sconv,
+			    "Linkname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+		}
 		/*
 		 * The following may seem odd, but: Technically, tar
 		 * does not store the file type for a "hard link"
@@ -980,7 +1130,7 @@
 			/* Old-style or GNU tar: we must ignore the size. */
 			archive_entry_set_size(entry, 0);
 			tar->entry_bytes_remaining = 0;
-		} else if (archive_read_format_tar_bid(a) > 50) {
+		} else if (archive_read_format_tar_bid(a, 50) > 50) {
 			/*
 			 * We don't know if it's pax: If the bid
 			 * function sees a valid ustar header
@@ -1005,7 +1155,13 @@
 		archive_entry_set_filetype(entry, AE_IFLNK);
 		archive_entry_set_size(entry, 0);
 		tar->entry_bytes_remaining = 0;
-		archive_entry_copy_symlink(entry, tar->entry_linkpath.s);
+		if (archive_entry_copy_symlink_l(entry, tar->entry_linkpath.s,
+		    archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) {
+			err = set_conversion_failed_error(a, tar->sconv,
+			    "Linkname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+		}
 		break;
 	case '3': /* Character device */
 		archive_entry_set_filetype(entry, AE_IFCHR);
@@ -1063,7 +1219,7 @@
 		archive_entry_set_filetype(entry, AE_IFREG);
 		break;
 	}
-	return (0);
+	return (err);
 }
 
 /*
@@ -1074,17 +1230,95 @@
     struct archive_entry *entry, const void *h)
 {
 	const struct archive_entry_header_ustar	*header;
+	int err = ARCHIVE_OK, err2;
 
 	/* Copy filename over (to ensure null termination). */
 	header = (const struct archive_entry_header_ustar *)h;
-	archive_strncpy(&(tar->entry_pathname), header->name, sizeof(header->name));
-	archive_entry_copy_pathname(entry, tar->entry_pathname.s);
+	if (archive_entry_copy_pathname_l(entry,
+	    header->name, sizeof(header->name), tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
 
 	/* Grab rest of common fields */
-	header_common(a, tar, entry, h);
+	err2 = header_common(a, tar, entry, h);
+	if (err > err2)
+		err = err2;
 
 	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
-	return (0);
+	return (err);
+}
+
+/*
+ * Read a Mac AppleDouble-encoded blob of file metadata,
+ * if there is one.
+ */
+static int
+read_mac_metadata_blob(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+	int64_t size;
+	const void *data;
+	const char *p, *name;
+	const wchar_t *wp, *wname;
+
+	(void)h; /* UNUSED */
+
+	wname = wp = archive_entry_pathname_w(entry);
+	if (wp != NULL) {
+		/* Find the last path element. */
+		for (; *wp != L'\0'; ++wp) {
+			if (wp[0] == '/' && wp[1] != L'\0')
+				wname = wp + 1;
+		}
+		/* 
+		 * If last path element starts with "._", then
+		 * this is a Mac extension.
+		 */
+		if (wname[0] != L'.' || wname[1] != L'_' || wname[2] == L'\0')
+			return ARCHIVE_OK;
+	} else {
+		/* Find the last path element. */
+		name = p = archive_entry_pathname(entry);
+		if (p == NULL)
+			return (ARCHIVE_FAILED);
+		for (; *p != '\0'; ++p) {
+			if (p[0] == '/' && p[1] != '\0')
+				name = p + 1;
+		}
+		/* 
+		 * If last path element starts with "._", then
+		 * this is a Mac extension.
+		 */
+		if (name[0] != '.' || name[1] != '_' || name[2] == '\0')
+			return ARCHIVE_OK;
+	}
+
+ 	/* Read the body as a Mac OS metadata blob. */
+	size = archive_entry_size(entry);
+
+	/*
+	 * TODO: Look beyond the body here to peek at the next header.
+	 * If it's a regular header (not an extension header)
+	 * that has the wrong name, just return the current
+	 * entry as-is, without consuming the body here.
+	 * That would reduce the risk of us mis-identifying
+	 * an ordinary file that just happened to have
+	 * a name starting with "._".
+	 *
+	 * Q: Is the above idea really possible?  Even
+	 * when there are GNU or pax extension entries?
+	 */
+	data = __archive_read_ahead(a, size, NULL);
+	if (data == NULL) {
+		*unconsumed = 0;
+		return (ARCHIVE_FATAL);
+	}
+	archive_entry_copy_mac_metadata(entry, data, size);
+	*unconsumed = (size + 511) & ~ 511;
+	tar_flush_unconsumed(a, unconsumed);
+	return (tar_read_header(a, tar, entry, unconsumed));
 }
 
 /*
@@ -1092,29 +1326,29 @@
  */
 static int
 header_pax_global(struct archive_read *a, struct tar *tar,
-    struct archive_entry *entry, const void *h)
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
 {
 	int err;
 
-	err = read_body_to_string(a, tar, &(tar->pax_global), h);
+	err = read_body_to_string(a, tar, &(tar->pax_global), h, unconsumed);
 	if (err != ARCHIVE_OK)
 		return (err);
-	err = tar_read_header(a, tar, entry);
+	err = tar_read_header(a, tar, entry, unconsumed);
 	return (err);
 }
 
 static int
 header_pax_extensions(struct archive_read *a, struct tar *tar,
-    struct archive_entry *entry, const void *h)
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
 {
 	int err, err2;
 
-	err = read_body_to_string(a, tar, &(tar->pax_header), h);
+	err = read_body_to_string(a, tar, &(tar->pax_header), h, unconsumed);
 	if (err != ARCHIVE_OK)
 		return (err);
 
 	/* Parse the next header. */
-	err = tar_read_header(a, tar, entry);
+	err = tar_read_header(a, tar, entry, unconsumed);
 	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
 		return (err);
 
@@ -1145,6 +1379,7 @@
 {
 	const struct archive_entry_header_ustar	*header;
 	struct archive_string *as;
+	int err = ARCHIVE_OK, r;
 
 	header = (const struct archive_entry_header_ustar *)h;
 
@@ -1155,22 +1390,37 @@
 		if (as->s[archive_strlen(as) - 1] != '/')
 			archive_strappend_char(as, '/');
 		archive_strncat(as, header->name, sizeof(header->name));
-	} else
+	} else {
 		archive_strncpy(as, header->name, sizeof(header->name));
-
-	archive_entry_copy_pathname(entry, as->s);
+	}
+	if (archive_entry_copy_pathname_l(entry, as->s, archive_strlen(as),
+	    tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
 
 	/* Handle rest of common fields. */
-	header_common(a, tar, entry, h);
+	r = header_common(a, tar, entry, h);
+	if (r == ARCHIVE_FATAL)
+		return (r);
+	if (r < err)
+		err = r;
 
 	/* Handle POSIX ustar fields. */
-	archive_strncpy(&(tar->entry_uname), header->uname,
-	    sizeof(header->uname));
-	archive_entry_copy_uname(entry, tar->entry_uname.s);
+	if (archive_entry_copy_uname_l(entry,
+	    header->uname, sizeof(header->uname), tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Uname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
 
-	archive_strncpy(&(tar->entry_gname), header->gname,
-	    sizeof(header->gname));
-	archive_entry_copy_gname(entry, tar->entry_gname.s);
+	if (archive_entry_copy_gname_l(entry,
+	    header->gname, sizeof(header->gname), tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Gname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
 
 	/* Parse out device numbers only for char and block specials. */
 	if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
@@ -1182,7 +1432,7 @@
 
 	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
 
-	return (0);
+	return (err);
 }
 
 
@@ -1198,6 +1448,8 @@
 	size_t attr_length, l, line_length;
 	char *p;
 	char *key, *value;
+	struct archive_string *as;
+	struct archive_string_conv *sconv;
 	int err, err2;
 
 	attr_length = strlen(attr);
@@ -1269,39 +1521,55 @@
 		value = p + 1;
 
 		/* Identify this attribute and set it in the entry. */
-		err2 = pax_attribute(tar, entry, key, value);
+		err2 = pax_attribute(a, tar, entry, key, value);
+		if (err2 == ARCHIVE_FATAL)
+			return (err2);
 		err = err_combine(err, err2);
 
 		/* Skip to next line */
 		attr += line_length;
 		attr_length -= line_length;
 	}
+
+	/*
+	 * PAX format uses UTF-8 as default charset for its metadata
+	 * unless hdrcharset=BINARY is present in its header.
+	 * We apply the charset specified by the hdrcharset option only
+	 * when the hdrcharset attribute(in PAX header) is BINARY because
+	 * we respect the charset described in PAX header and BINARY also
+	 * means that metadata(filename,uname and gname) character-set
+	 * is unknown.
+	 */
+	if (tar->pax_hdrcharset_binary)
+		sconv = tar->opt_sconv;
+	else {
+		sconv = archive_string_conversion_from_charset(
+		    &(a->archive), "UTF-8", 1);
+		if (sconv == NULL)
+			return (ARCHIVE_FATAL);
+		if (tar->compat_2x)
+			archive_string_conversion_set_opt(sconv,
+			    SCONV_SET_OPT_UTF8_LIBARCHIVE2X);
+	}
+
 	if (archive_strlen(&(tar->entry_gname)) > 0) {
-		value = tar->entry_gname.s;
-		if (tar->pax_hdrcharset_binary)
-			archive_entry_copy_gname(entry, value);
-		else {
-			if (!archive_entry_update_gname_utf8(entry, value)) {
-				err = ARCHIVE_WARN;
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Gname in pax header can't "
-				    "be converted to current locale.");
-			}
+		if (archive_entry_copy_gname_l(entry, tar->entry_gname.s,
+		    archive_strlen(&(tar->entry_gname)), sconv) != 0) {
+			err = set_conversion_failed_error(a, sconv, "Gname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+			/* Use a converted an original name. */
+			archive_entry_copy_gname(entry, tar->entry_gname.s);
 		}
 	}
 	if (archive_strlen(&(tar->entry_linkpath)) > 0) {
-		value = tar->entry_linkpath.s;
-		if (tar->pax_hdrcharset_binary)
-			archive_entry_copy_link(entry, value);
-		else {
-			if (!archive_entry_update_link_utf8(entry, value)) {
-				err = ARCHIVE_WARN;
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Linkname in pax header can't "
-				    "be converted to current locale.");
-			}
+		if (archive_entry_copy_link_l(entry, tar->entry_linkpath.s,
+		    archive_strlen(&(tar->entry_linkpath)), sconv) != 0) {
+			err = set_conversion_failed_error(a, sconv, "Linkname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+			/* Use a converted an original name. */
+			archive_entry_copy_link(entry, tar->entry_linkpath.s);
 		}
 	}
 	/*
@@ -1313,36 +1581,29 @@
 	 * we find and figure it all out afterwards.  This is the
 	 * figuring out part.
 	 */
-	value = NULL;
+	as = NULL;
 	if (archive_strlen(&(tar->entry_pathname_override)) > 0)
-		value = tar->entry_pathname_override.s;
+		as = &(tar->entry_pathname_override);
 	else if (archive_strlen(&(tar->entry_pathname)) > 0)
-		value = tar->entry_pathname.s;
-	if (value != NULL) {
-		if (tar->pax_hdrcharset_binary)
-			archive_entry_copy_pathname(entry, value);
-		else {
-			if (!archive_entry_update_pathname_utf8(entry, value)) {
-				err = ARCHIVE_WARN;
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Pathname in pax header can't be "
-				    "converted to current locale.");
-			}
+		as = &(tar->entry_pathname);
+	if (as != NULL) {
+		if (archive_entry_copy_pathname_l(entry, as->s,
+		    archive_strlen(as), sconv) != 0) {
+			err = set_conversion_failed_error(a, sconv, "Pathname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+			/* Use a converted an original name. */
+			archive_entry_copy_pathname(entry, as->s);
 		}
 	}
 	if (archive_strlen(&(tar->entry_uname)) > 0) {
-		value = tar->entry_uname.s;
-		if (tar->pax_hdrcharset_binary)
-			archive_entry_copy_uname(entry, value);
-		else {
-			if (!archive_entry_update_uname_utf8(entry, value)) {
-				err = ARCHIVE_WARN;
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Uname in pax header can't "
-				    "be converted to current locale.");
-			}
+		if (archive_entry_copy_uname_l(entry, tar->entry_uname.s,
+		    archive_strlen(&(tar->entry_uname)), sconv) != 0) {
+			err = set_conversion_failed_error(a, sconv, "Uname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+			/* Use a converted an original name. */
+			archive_entry_copy_uname(entry, tar->entry_uname.s);
 		}
 	}
 	return (err);
@@ -1356,7 +1617,7 @@
 	void *value_decoded;
 	size_t value_len;
 
-	if (strlen(name) < 18 || (strncmp(name, "LIBARCHIVE.xattr.", 17)) != 0)
+	if (strlen(name) < 18 || (memcmp(name, "LIBARCHIVE.xattr.", 17)) != 0)
 		return 3;
 
 	name += 17;
@@ -1395,12 +1656,12 @@
  * any of them look useful.
  */
 static int
-pax_attribute(struct tar *tar, struct archive_entry *entry,
-    char *key, char *value)
+pax_attribute(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, char *key, char *value)
 {
 	int64_t s;
 	long n;
-	wchar_t *wp;
+	int err = ARCHIVE_OK, r;
 
 	switch (key[0]) {
 	case 'G':
@@ -1414,8 +1675,10 @@
 		if (strcmp(key, "GNU.sparse.offset") == 0) {
 			tar->sparse_offset = tar_atol10(value, strlen(value));
 			if (tar->sparse_numbytes != -1) {
-				gnu_add_sparse_entry(tar,
-				    tar->sparse_offset, tar->sparse_numbytes);
+				if (gnu_add_sparse_entry(a, tar,
+				    tar->sparse_offset, tar->sparse_numbytes)
+				    != ARCHIVE_OK)
+					return (ARCHIVE_FATAL);
 				tar->sparse_offset = -1;
 				tar->sparse_numbytes = -1;
 			}
@@ -1423,8 +1686,10 @@
 		if (strcmp(key, "GNU.sparse.numbytes") == 0) {
 			tar->sparse_numbytes = tar_atol10(value, strlen(value));
 			if (tar->sparse_numbytes != -1) {
-				gnu_add_sparse_entry(tar,
-				    tar->sparse_offset, tar->sparse_numbytes);
+				if (gnu_add_sparse_entry(a, tar,
+				    tar->sparse_offset, tar->sparse_numbytes)
+				    != ARCHIVE_OK)
+					return (ARCHIVE_FATAL);
 				tar->sparse_offset = -1;
 				tar->sparse_numbytes = -1;
 			}
@@ -1438,7 +1703,7 @@
 		if (strcmp(key, "GNU.sparse.map") == 0) {
 			tar->sparse_gnu_major = 0;
 			tar->sparse_gnu_minor = 1;
-			if (gnu_sparse_01_parse(tar, value) != ARCHIVE_OK)
+			if (gnu_sparse_01_parse(a, tar, value) != ARCHIVE_OK)
 				return (ARCHIVE_WARN);
 		}
 
@@ -1469,99 +1734,146 @@
 		/* Our extensions */
 /* TODO: Handle arbitrary extended attributes... */
 /*
-		if (strcmp(key, "LIBARCHIVE.xxxxxxx")==0)
+		if (strcmp(key, "LIBARCHIVE.xxxxxxx") == 0)
 			archive_entry_set_xxxxxx(entry, value);
 */
-		if (strcmp(key, "LIBARCHIVE.creationtime")==0) {
+		if (strcmp(key, "LIBARCHIVE.creationtime") == 0) {
 			pax_time(value, &s, &n);
 			archive_entry_set_birthtime(entry, s, n);
 		}
-		if (strncmp(key, "LIBARCHIVE.xattr.", 17)==0)
+		if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0)
 			pax_attribute_xattr(entry, key, value);
 		break;
 	case 'S':
 		/* We support some keys used by the "star" archiver */
-		if (strcmp(key, "SCHILY.acl.access")==0) {
-			wp = utf8_decode(tar, value, strlen(value));
-			/* TODO: if (wp == NULL) */
-			__archive_entry_acl_parse_w(entry, wp,
-			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
-		} else if (strcmp(key, "SCHILY.acl.default")==0) {
-			wp = utf8_decode(tar, value, strlen(value));
-			/* TODO: if (wp == NULL) */
-			__archive_entry_acl_parse_w(entry, wp,
-			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
-		} else if (strcmp(key, "SCHILY.devmajor")==0) {
+		if (strcmp(key, "SCHILY.acl.access") == 0) {
+			if (tar->sconv_acl == NULL) {
+				tar->sconv_acl =
+				    archive_string_conversion_from_charset(
+					&(a->archive), "UTF-8", 1);
+				if (tar->sconv_acl == NULL)
+					return (ARCHIVE_FATAL);
+			}
+
+			r = archive_acl_parse_l(archive_entry_acl(entry),
+			    value, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+			    tar->sconv_acl);
+			if (r != ARCHIVE_OK) {
+				err = r;
+				if (err == ARCHIVE_FATAL) {
+					archive_set_error(&a->archive, ENOMEM,
+					    "Can't allocate memory for "
+					    "SCHILY.acl.access");
+					return (err);
+				}
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Parse error: SCHILY.acl.access");
+			}
+		} else if (strcmp(key, "SCHILY.acl.default") == 0) {
+			if (tar->sconv_acl == NULL) {
+				tar->sconv_acl =
+				    archive_string_conversion_from_charset(
+					&(a->archive), "UTF-8", 1);
+				if (tar->sconv_acl == NULL)
+					return (ARCHIVE_FATAL);
+			}
+
+			r = archive_acl_parse_l(archive_entry_acl(entry),
+			    value, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
+			    tar->sconv_acl);
+			if (r != ARCHIVE_OK) {
+				err = r;
+				if (err == ARCHIVE_FATAL) {
+					archive_set_error(&a->archive, ENOMEM,
+					    "Can't allocate memory for "
+					    "SCHILY.acl.default");
+					return (err);
+				}
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Parse error: SCHILY.acl.default");
+			}
+		} else if (strcmp(key, "SCHILY.devmajor") == 0) {
 			archive_entry_set_rdevmajor(entry,
 			    tar_atol10(value, strlen(value)));
-		} else if (strcmp(key, "SCHILY.devminor")==0) {
+		} else if (strcmp(key, "SCHILY.devminor") == 0) {
 			archive_entry_set_rdevminor(entry,
 			    tar_atol10(value, strlen(value)));
-		} else if (strcmp(key, "SCHILY.fflags")==0) {
+		} else if (strcmp(key, "SCHILY.fflags") == 0) {
 			archive_entry_copy_fflags_text(entry, value);
-		} else if (strcmp(key, "SCHILY.dev")==0) {
+		} else if (strcmp(key, "SCHILY.dev") == 0) {
 			archive_entry_set_dev(entry,
 			    tar_atol10(value, strlen(value)));
-		} else if (strcmp(key, "SCHILY.ino")==0) {
+		} else if (strcmp(key, "SCHILY.ino") == 0) {
 			archive_entry_set_ino(entry,
 			    tar_atol10(value, strlen(value)));
-		} else if (strcmp(key, "SCHILY.nlink")==0) {
+		} else if (strcmp(key, "SCHILY.nlink") == 0) {
 			archive_entry_set_nlink(entry,
 			    tar_atol10(value, strlen(value)));
-		} else if (strcmp(key, "SCHILY.realsize")==0) {
+		} else if (strcmp(key, "SCHILY.realsize") == 0) {
 			tar->realsize = tar_atol10(value, strlen(value));
 			archive_entry_set_size(entry, tar->realsize);
+		} else if (strcmp(key, "SUN.holesdata") == 0) {
+			/* A Solaris extension for sparse. */
+			r = solaris_sparse_parse(a, tar, entry, value);
+			if (r < err) {
+				if (r == ARCHIVE_FATAL)
+					return (r);
+				err = r;
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Parse error: SUN.holesdata");
+			}
 		}
 		break;
 	case 'a':
-		if (strcmp(key, "atime")==0) {
+		if (strcmp(key, "atime") == 0) {
 			pax_time(value, &s, &n);
 			archive_entry_set_atime(entry, s, n);
 		}
 		break;
 	case 'c':
-		if (strcmp(key, "ctime")==0) {
+		if (strcmp(key, "ctime") == 0) {
 			pax_time(value, &s, &n);
 			archive_entry_set_ctime(entry, s, n);
-		} else if (strcmp(key, "charset")==0) {
+		} else if (strcmp(key, "charset") == 0) {
 			/* TODO: Publish charset information in entry. */
-		} else if (strcmp(key, "comment")==0) {
+		} else if (strcmp(key, "comment") == 0) {
 			/* TODO: Publish comment in entry. */
 		}
 		break;
 	case 'g':
-		if (strcmp(key, "gid")==0) {
+		if (strcmp(key, "gid") == 0) {
 			archive_entry_set_gid(entry,
 			    tar_atol10(value, strlen(value)));
-		} else if (strcmp(key, "gname")==0) {
+		} else if (strcmp(key, "gname") == 0) {
 			archive_strcpy(&(tar->entry_gname), value);
 		}
 		break;
 	case 'h':
 		if (strcmp(key, "hdrcharset") == 0) {
 			if (strcmp(value, "BINARY") == 0)
+				/* Binary  mode. */
 				tar->pax_hdrcharset_binary = 1;
 			else if (strcmp(value, "ISO-IR 10646 2000 UTF-8") == 0)
 				tar->pax_hdrcharset_binary = 0;
-			else {
-				/* TODO: Warn about unsupported hdrcharset */
-			}
 		}
 		break;
 	case 'l':
 		/* pax interchange doesn't distinguish hardlink vs. symlink. */
-		if (strcmp(key, "linkpath")==0) {
+		if (strcmp(key, "linkpath") == 0) {
 			archive_strcpy(&(tar->entry_linkpath), value);
 		}
 		break;
 	case 'm':
-		if (strcmp(key, "mtime")==0) {
+		if (strcmp(key, "mtime") == 0) {
 			pax_time(value, &s, &n);
 			archive_entry_set_mtime(entry, s, n);
 		}
 		break;
 	case 'p':
-		if (strcmp(key, "path")==0) {
+		if (strcmp(key, "path") == 0) {
 			archive_strcpy(&(tar->entry_pathname), value);
 		}
 		break;
@@ -1570,8 +1882,8 @@
 		break;
 	case 's':
 		/* POSIX has reserved 'security.*' */
-		/* Someday: if (strcmp(key, "security.acl")==0) { ... } */
-		if (strcmp(key, "size")==0) {
+		/* Someday: if (strcmp(key, "security.acl") == 0) { ... } */
+		if (strcmp(key, "size") == 0) {
 			/* "size" is the size of the data in the entry. */
 			tar->entry_bytes_remaining
 			    = tar_atol10(value, strlen(value));
@@ -1592,15 +1904,15 @@
 		}
 		break;
 	case 'u':
-		if (strcmp(key, "uid")==0) {
+		if (strcmp(key, "uid") == 0) {
 			archive_entry_set_uid(entry,
 			    tar_atol10(value, strlen(value)));
-		} else if (strcmp(key, "uname")==0) {
+		} else if (strcmp(key, "uname") == 0) {
 			archive_strcpy(&(tar->entry_uname), value);
 		}
 		break;
 	}
-	return (0);
+	return (err);
 }
 
 
@@ -1660,11 +1972,11 @@
  */
 static int
 header_gnutar(struct archive_read *a, struct tar *tar,
-    struct archive_entry *entry, const void *h)
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
 {
 	const struct archive_entry_header_gnutar *header;
-
-	(void)a;
+	int64_t t;
+	int err = ARCHIVE_OK;
 
 	/*
 	 * GNU header is like POSIX ustar, except 'prefix' is
@@ -1673,25 +1985,36 @@
 	 */
 
 	/* Grab fields common to all tar variants. */
-	header_common(a, tar, entry, h);
+	err = header_common(a, tar, entry, h);
+	if (err == ARCHIVE_FATAL)
+		return (err);
 
 	/* Copy filename over (to ensure null termination). */
 	header = (const struct archive_entry_header_gnutar *)h;
-	archive_strncpy(&(tar->entry_pathname), header->name,
-	    sizeof(header->name));
-	archive_entry_copy_pathname(entry, tar->entry_pathname.s);
+	if (archive_entry_copy_pathname_l(entry,
+	    header->name, sizeof(header->name), tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
 
 	/* Fields common to ustar and GNU */
 	/* XXX Can the following be factored out since it's common
 	 * to ustar and gnu tar?  Is it okay to move it down into
 	 * header_common, perhaps?  */
-	archive_strncpy(&(tar->entry_uname),
-	    header->uname, sizeof(header->uname));
-	archive_entry_copy_uname(entry, tar->entry_uname.s);
+	if (archive_entry_copy_uname_l(entry,
+	    header->uname, sizeof(header->uname), tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Uname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
 
-	archive_strncpy(&(tar->entry_gname),
-	    header->gname, sizeof(header->gname));
-	archive_entry_copy_gname(entry, tar->entry_gname.s);
+	if (archive_entry_copy_gname_l(entry,
+	    header->gname, sizeof(header->gname), tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Gname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
 
 	/* Parse out device numbers only for char and block specials */
 	if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
@@ -1705,10 +2028,13 @@
 	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
 
 	/* Grab GNU-specific fields. */
-	archive_entry_set_atime(entry,
-	    tar_atol(header->atime, sizeof(header->atime)), 0);
-	archive_entry_set_ctime(entry,
-	    tar_atol(header->ctime, sizeof(header->ctime)), 0);
+	t = tar_atol(header->atime, sizeof(header->atime));
+	if (t > 0)
+		archive_entry_set_atime(entry, t, 0);
+	t = tar_atol(header->ctime, sizeof(header->ctime));
+	if (t > 0)
+		archive_entry_set_ctime(entry, t, 0);
+
 	if (header->realsize[0] != 0) {
 		tar->realsize
 		    = tar_atol(header->realsize, sizeof(header->realsize));
@@ -1716,24 +2042,29 @@
 	}
 
 	if (header->sparse[0].offset[0] != 0) {
-		gnu_sparse_old_read(a, tar, header);
+		if (gnu_sparse_old_read(a, tar, header, unconsumed)
+		    != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
 	} else {
 		if (header->isextended[0] != 0) {
 			/* XXX WTF? XXX */
 		}
 	}
 
-	return (0);
+	return (err);
 }
 
-static void
-gnu_add_sparse_entry(struct tar *tar, off_t offset, off_t remaining)
+static int
+gnu_add_sparse_entry(struct archive_read *a, struct tar *tar,
+    int64_t offset, int64_t remaining)
 {
 	struct sparse_block *p;
 
 	p = (struct sparse_block *)malloc(sizeof(*p));
-	if (p == NULL)
-		__archive_errx(1, "Out of memory");
+	if (p == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
 	memset(p, 0, sizeof(*p));
 	if (tar->sparse_last != NULL)
 		tar->sparse_last->next = p;
@@ -1742,6 +2073,7 @@
 	tar->sparse_last = p;
 	p->offset = offset;
 	p->remaining = remaining;
+	return (ARCHIVE_OK);
 }
 
 static void
@@ -1771,7 +2103,7 @@
 
 static int
 gnu_sparse_old_read(struct archive_read *a, struct tar *tar,
-    const struct archive_entry_header_gnutar *header)
+    const struct archive_entry_header_gnutar *header, size_t *unconsumed)
 {
 	ssize_t bytes_read;
 	const void *data;
@@ -1782,11 +2114,13 @@
 	};
 	const struct extended *ext;
 
-	gnu_sparse_old_parse(tar, header->sparse, 4);
+	if (gnu_sparse_old_parse(a, tar, header->sparse, 4) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
 	if (header->isextended[0] == 0)
 		return (ARCHIVE_OK);
 
 	do {
+		tar_flush_unconsumed(a, unconsumed);
 		data = __archive_read_ahead(a, 512, &bytes_read);
 		if (bytes_read < 0)
 			return (ARCHIVE_FATAL);
@@ -1796,26 +2130,30 @@
 			    "detected while reading sparse file data");
 			return (ARCHIVE_FATAL);
 		}
-		__archive_read_consume(a, 512);
+		*unconsumed = 512;
 		ext = (const struct extended *)data;
-		gnu_sparse_old_parse(tar, ext->sparse, 21);
+		if (gnu_sparse_old_parse(a, tar, ext->sparse, 21) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
 	} while (ext->isextended[0] != 0);
 	if (tar->sparse_list != NULL)
 		tar->entry_offset = tar->sparse_list->offset;
 	return (ARCHIVE_OK);
 }
 
-static void
-gnu_sparse_old_parse(struct tar *tar,
+static int
+gnu_sparse_old_parse(struct archive_read *a, struct tar *tar,
     const struct gnu_sparse *sparse, int length)
 {
 	while (length > 0 && sparse->offset[0] != 0) {
-		gnu_add_sparse_entry(tar,
+		if (gnu_add_sparse_entry(a, tar,
 		    tar_atol(sparse->offset, sizeof(sparse->offset)),
-		    tar_atol(sparse->numbytes, sizeof(sparse->numbytes)));
+		    tar_atol(sparse->numbytes, sizeof(sparse->numbytes)))
+		    != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
 		sparse++;
 		length--;
 	}
+	return (ARCHIVE_OK);
 }
 
 /*
@@ -1824,7 +2162,7 @@
  * Beginning with GNU tar 1.15, sparse files are stored using
  * information in the pax extended header.  The GNU tar maintainers
  * have gone through a number of variations in the process of working
- * out this scheme; furtunately, they're all numbered.
+ * out this scheme; fortunately, they're all numbered.
  *
  * Sparse format 0.0 uses attribute GNU.sparse.numblocks to store the
  * number of blocks, and GNU.sparse.offset/GNU.sparse.numbytes to
@@ -1845,10 +2183,10 @@
  */
 
 static int
-gnu_sparse_01_parse(struct tar *tar, const char *p)
+gnu_sparse_01_parse(struct archive_read *a, struct tar *tar, const char *p)
 {
 	const char *e;
-	off_t offset = -1, size = -1;
+	int64_t offset = -1, size = -1;
 
 	for (;;) {
 		e = p;
@@ -1865,7 +2203,9 @@
 			size = tar_atol10(p, e - p);
 			if (size < 0)
 				return (ARCHIVE_WARN);
-			gnu_add_sparse_entry(tar, offset, size);
+			if (gnu_add_sparse_entry(a, tar, offset, size)
+			    != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 			offset = -1;
 		}
 		if (*e == '\0')
@@ -1899,7 +2239,7 @@
  */
 static int64_t
 gnu_sparse_10_atol(struct archive_read *a, struct tar *tar,
-    ssize_t *remaining)
+    int64_t *remaining, size_t *unconsumed)
 {
 	int64_t l, limit, last_digit_limit;
 	const char *p;
@@ -1915,7 +2255,7 @@
 	 * don't require this, but they should.
 	 */
 	do {
-		bytes_read = readline(a, tar, &p, tar_min(*remaining, 100));
+		bytes_read = readline(a, tar, &p, tar_min(*remaining, 100), unconsumed);
 		if (bytes_read <= 0)
 			return (ARCHIVE_FATAL);
 		*remaining -= bytes_read;
@@ -1944,11 +2284,11 @@
  * that was read.
  */
 static ssize_t
-gnu_sparse_10_read(struct archive_read *a, struct tar *tar)
+gnu_sparse_10_read(struct archive_read *a, struct tar *tar, size_t *unconsumed)
 {
-	ssize_t remaining, bytes_read;
+	ssize_t bytes_read;
 	int entries;
-	off_t offset, size, to_skip;
+	int64_t offset, size, to_skip, remaining;
 
 	/* Clear out the existing sparse list. */
 	gnu_clear_sparse_list(tar);
@@ -1956,29 +2296,76 @@
 	remaining = tar->entry_bytes_remaining;
 
 	/* Parse entries. */
-	entries = gnu_sparse_10_atol(a, tar, &remaining);
+	entries = gnu_sparse_10_atol(a, tar, &remaining, unconsumed);
 	if (entries < 0)
 		return (ARCHIVE_FATAL);
 	/* Parse the individual entries. */
 	while (entries-- > 0) {
 		/* Parse offset/size */
-		offset = gnu_sparse_10_atol(a, tar, &remaining);
+		offset = gnu_sparse_10_atol(a, tar, &remaining, unconsumed);
 		if (offset < 0)
 			return (ARCHIVE_FATAL);
-		size = gnu_sparse_10_atol(a, tar, &remaining);
+		size = gnu_sparse_10_atol(a, tar, &remaining, unconsumed);
 		if (size < 0)
 			return (ARCHIVE_FATAL);
 		/* Add a new sparse entry. */
-		gnu_add_sparse_entry(tar, offset, size);
+		if (gnu_add_sparse_entry(a, tar, offset, size) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
 	}
 	/* Skip rest of block... */
+	tar_flush_unconsumed(a, unconsumed);
 	bytes_read = tar->entry_bytes_remaining - remaining;
 	to_skip = 0x1ff & -bytes_read;
-	if (to_skip != __archive_read_skip(a, to_skip))
+	if (to_skip != __archive_read_consume(a, to_skip))
 		return (ARCHIVE_FATAL);
 	return (bytes_read + to_skip);
 }
 
+/*
+ * Solaris pax extension for a sparse file. This is recorded with the
+ * data and hole pairs. The way recording sparse information by Solaris'
+ * pax simply indicates where data and sparse are, so the stored contents
+ * consist of both data and hole.
+ */
+static int
+solaris_sparse_parse(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const char *p)
+{
+	const char *e;
+	int64_t start, end;
+	int hole = 1;
+
+	(void)entry; /* UNUSED */
+
+	end = 0;
+	if (*p == ' ')
+		p++;
+	else
+		return (ARCHIVE_WARN);
+	for (;;) {
+		e = p;
+		while (*e != '\0' && *e != ' ') {
+			if (*e < '0' || *e > '9')
+				return (ARCHIVE_WARN);
+			e++;
+		}
+		start = end;
+		end = tar_atol10(p, e - p);
+		if (end < 0)
+			return (ARCHIVE_WARN);
+		if (start < end) {
+			if (gnu_add_sparse_entry(a, tar, start,
+			    end - start) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			tar->sparse_last->hole = hole;
+		}
+		if (*e == '\0')
+			return (ARCHIVE_OK);
+		p = e + 1;
+		hole = hole == 0;
+	}
+}
+
 /*-
  * Convert text->integer.
  *
@@ -2121,7 +2508,7 @@
  */
 static ssize_t
 readline(struct archive_read *a, struct tar *tar, const char **start,
-    ssize_t limit)
+    ssize_t limit, size_t *unconsumed)
 {
 	ssize_t bytes_read;
 	ssize_t total_size = 0;
@@ -2129,6 +2516,8 @@
 	const char *s;
 	void *p;
 
+	tar_flush_unconsumed(a, unconsumed);
+
 	t = __archive_read_ahead(a, 1, &bytes_read);
 	if (bytes_read <= 0)
 		return (ARCHIVE_FATAL);
@@ -2143,10 +2532,11 @@
 			    "Line too long");
 			return (ARCHIVE_FATAL);
 		}
-		__archive_read_consume(a, bytes_read);
+		*unconsumed = bytes_read;
 		*start = s;
 		return (bytes_read);
 	}
+	*unconsumed = bytes_read;
 	/* Otherwise, we need to accumulate in a line buffer. */
 	for (;;) {
 		if (total_size + bytes_read > limit) {
@@ -2161,7 +2551,7 @@
 			return (ARCHIVE_FATAL);
 		}
 		memcpy(tar->line.s + total_size, t, bytes_read);
-		__archive_read_consume(a, bytes_read);
+		tar_flush_unconsumed(a, unconsumed);
 		total_size += bytes_read;
 		/* If we found '\n', clean up and return. */
 		if (p != NULL) {
@@ -2178,122 +2568,10 @@
 		if (p != NULL) {
 			bytes_read = 1 + ((const char *)p) - s;
 		}
+		*unconsumed = bytes_read;
 	}
 }
 
-static wchar_t *
-utf8_decode(struct tar *tar, const char *src, size_t length)
-{
-	wchar_t *dest;
-	ssize_t n;
-
-	/* Ensure pax_entry buffer is big enough. */
-	if (tar->pax_entry_length <= length) {
-		wchar_t *old_entry;
-
-		if (tar->pax_entry_length <= 0)
-			tar->pax_entry_length = 1024;
-		while (tar->pax_entry_length <= length + 1)
-			tar->pax_entry_length *= 2;
-
-		old_entry = tar->pax_entry;
-		tar->pax_entry = (wchar_t *)realloc(tar->pax_entry,
-		    tar->pax_entry_length * sizeof(wchar_t));
-		if (tar->pax_entry == NULL) {
-			free(old_entry);
-			/* TODO: Handle this error. */
-			return (NULL);
-		}
-	}
-
-	dest = tar->pax_entry;
-	while (length > 0) {
-		n = UTF8_mbrtowc(dest, src, length);
-		if (n < 0)
-			return (NULL);
-		if (n == 0)
-			break;
-		dest++;
-		src += n;
-		length -= n;
-	}
-	*dest = L'\0';
-	return (tar->pax_entry);
-}
-
-/*
- * Copied and simplified from FreeBSD libc/locale.
- */
-static ssize_t
-UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n)
-{
-        int ch, i, len, mask;
-        unsigned long wch;
-
-        if (s == NULL || n == 0 || pwc == NULL)
-                return (0);
-
-        /*
-         * Determine the number of octets that make up this character from
-         * the first octet, and a mask that extracts the interesting bits of
-         * the first octet.
-         */
-        ch = (unsigned char)*s;
-        if ((ch & 0x80) == 0) {
-                mask = 0x7f;
-                len = 1;
-        } else if ((ch & 0xe0) == 0xc0) {
-                mask = 0x1f;
-                len = 2;
-        } else if ((ch & 0xf0) == 0xe0) {
-                mask = 0x0f;
-                len = 3;
-        } else if ((ch & 0xf8) == 0xf0) {
-                mask = 0x07;
-                len = 4;
-        } else {
-		/* Invalid first byte. */
-		return (-1);
-        }
-
-        if (n < (size_t)len) {
-		/* Valid first byte but truncated. */
-                return (-2);
-	}
-
-        /*
-         * Decode the octet sequence representing the character in chunks
-         * of 6 bits, most significant first.
-         */
-        wch = (unsigned char)*s++ & mask;
-        i = len;
-        while (--i != 0) {
-                if ((*s & 0xc0) != 0x80) {
-			/* Invalid intermediate byte; consume one byte and
-			 * emit '?' */
-			*pwc = '?';
-			return (1);
-                }
-                wch <<= 6;
-                wch |= *s++ & 0x3f;
-        }
-
-	/* Assign the value to the output; out-of-range values
-	 * just get truncated. */
-	*pwc = (wchar_t)wch;
-#ifdef WCHAR_MAX
-	/*
-	 * If platform has WCHAR_MAX, we can do something
-	 * more sensible with out-of-range values.
-	 */
-	if (wch >= WCHAR_MAX)
-		*pwc = '?';
-#endif
-	/* Return number of bytes input consumed: 0 for end-of-string. */
-        return (wch == L'\0' ? 0 : len);
-}
-
-
 /*
  * base64_decode - Base64 decode
  *
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_support_format_xar.c
--- a/head/contrib/libarchive/libarchive/archive_read_support_format_xar.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_support_format_xar.c	Fri Mar 02 16:54:40 2012 +0200
@@ -23,12 +23,11 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_xar.c 228772 2011-12-21 15:06:01Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_xar.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
-#include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
@@ -52,9 +51,10 @@
 #endif
 
 #include "archive.h"
+#include "archive_crypto_private.h"
 #include "archive_endian.h"
 #include "archive_entry.h"
-#include "archive_hash.h"
+#include "archive_entry_locale.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
 
@@ -74,6 +74,8 @@
 archive_read_support_format_xar(struct archive *_a)
 {
 	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_xar");
 
 	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 	    "Xar not supported on this platform");
@@ -82,8 +84,8 @@
 
 #else	/* Support xar format */
 
-//#define DEBUG 1
-//#define DEBUG_PRINT_TOC 1
+/* #define DEBUG 1 */
+/* #define DEBUG_PRINT_TOC 1 */
 #if DEBUG_PRINT_TOC
 #define PRINT_TOC(d, outbytes)	do {				\
 	unsigned char *x = (unsigned char *)(uintptr_t)d;	\
@@ -303,7 +305,8 @@
 	int64_t			 total;
 	uint64_t		 h_base;
 	int			 end_of_file;
-	unsigned char		 buff[1024*32];
+#define OUTBUFF_SIZE	(1024 * 64)
+	unsigned char		*outbuff;
 
 	enum xmlstatus		 xmlsts;
 	enum xmlstatus		 xmlsts_unknown;
@@ -350,10 +353,13 @@
 	int	 		 entry_init;
 	uint64_t		 entry_total;
 	uint64_t		 entry_remaining;
+	size_t			 entry_unconsumed;
 	uint64_t		 entry_size;
 	enum enctype 		 entry_encoding;
 	struct chksumval	 entry_a_sum;
 	struct chksumval	 entry_e_sum;
+
+	struct archive_string_conv *sconv;
 };
 
 struct xmlattr {
@@ -367,11 +373,11 @@
 	struct xmlattr	**last;
 };
 
-static int	xar_bid(struct archive_read *);
+static int	xar_bid(struct archive_read *, int);
 static int	xar_read_header(struct archive_read *,
 		    struct archive_entry *);
 static int	xar_read_data(struct archive_read *,
-		    const void **, size_t *, off_t *);
+		    const void **, size_t *, int64_t *);
 static int	xar_read_data_skip(struct archive_read *);
 static int	xar_cleanup(struct archive_read *);
 static int	move_reading_point(struct archive_read *, uint64_t);
@@ -383,9 +389,11 @@
 static int64_t	atol8(const char *, size_t);
 static size_t	atohex(unsigned char *, size_t, const char *, size_t);
 static time_t	parse_time(const char *p, size_t n);
-static void	heap_add_entry(struct heap_queue *, struct xar_file *);
+static int	heap_add_entry(struct archive_read *a,
+    struct heap_queue *, struct xar_file *);
 static struct xar_file *heap_get_entry(struct heap_queue *);
-static void	add_link(struct xar *, struct xar_file *);
+static int	add_link(struct archive_read *,
+    struct xar *, struct xar_file *);
 static void	checksum_init(struct archive_read *, int, int);
 static void	checksum_update(struct archive_read *, const void *,
 		    size_t, const void *, size_t);
@@ -396,28 +404,38 @@
 		    size_t *, const void *, size_t *);
 static int	decompression_cleanup(struct archive_read *);
 static void	xmlattr_cleanup(struct xmlattr_list *);
-static void	file_new(struct xar *, struct xmlattr_list *);
+static int	file_new(struct archive_read *,
+    struct xar *, struct xmlattr_list *);
 static void	file_free(struct xar_file *);
-static void	xattr_new(struct xar *, struct xmlattr_list *);
+static int	xattr_new(struct archive_read *,
+    struct xar *, struct xmlattr_list *);
 static void	xattr_free(struct xattr *);
 static int	getencoding(struct xmlattr_list *);
 static int	getsumalgorithm(struct xmlattr_list *);
-static void	unknowntag_start(struct xar *, const char *);
+static int	unknowntag_start(struct archive_read *,
+    struct xar *, const char *);
 static void	unknowntag_end(struct xar *, const char *);
-static void	xml_start(void *, const char *, struct xmlattr_list *);
+static int	xml_start(struct archive_read *,
+    const char *, struct xmlattr_list *);
 static void	xml_end(void *, const char *);
 static void	xml_data(void *, const char *, int);
 static int	xml_parse_file_flags(struct xar *, const char *);
 static int	xml_parse_file_ext2(struct xar *, const char *);
 #if defined(HAVE_LIBXML_XMLREADER_H)
-static int	xml2_xmlattr_setup(struct xmlattr_list *, xmlTextReaderPtr);
+static int	xml2_xmlattr_setup(struct archive_read *,
+    struct xmlattr_list *, xmlTextReaderPtr);
 static int	xml2_read_cb(void *, char *, int);
 static int	xml2_close_cb(void *);
 static void	xml2_error_hdr(void *, const char *, xmlParserSeverities,
 		    xmlTextReaderLocatorPtr);
 static int	xml2_read_toc(struct archive_read *);
 #elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
-static void	expat_xmlattr_setup(struct xmlattr_list *, const XML_Char **);
+struct expat_userData {
+	int state;
+	struct archive_read *archive;
+};
+static int	expat_xmlattr_setup(struct archive_read *,
+    struct xmlattr_list *, const XML_Char **);
 static void	expat_start_cb(void *, const XML_Char *, const XML_Char **);
 static void	expat_end_cb(void *, const XML_Char *);
 static void	expat_data_cb(void *, const XML_Char *, int);
@@ -431,6 +449,9 @@
 	struct archive_read *a = (struct archive_read *)_a;
 	int r;
 
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_xar");
+
 	xar = (struct xar *)calloc(1, sizeof(*xar));
 	if (xar == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
@@ -453,11 +474,13 @@
 }
 
 static int
-xar_bid(struct archive_read *a)
+xar_bid(struct archive_read *a, int best_bid)
 {
 	const unsigned char *b;
 	int bid;
 
+	(void)best_bid; /* UNUSED */
+
 	b = __archive_read_ahead(a, HEADER_SIZE, NULL);
 	if (b == NULL)
 		return (-1);
@@ -638,8 +661,17 @@
 	int r;
 
 	xar = (struct xar *)(a->format->data);
+	r = ARCHIVE_OK;
 
 	if (xar->offset == 0) {
+		/* Create a character conversion object. */
+		if (xar->sconv == NULL) {
+			xar->sconv = archive_string_conversion_from_charset(
+			    &(a->archive), "UTF-8", 1);
+			if (xar->sconv == NULL)
+				return (ARCHIVE_FATAL);
+		}
+
 		/* Read TOC. */
 		r = read_toc(a);
 		if (r != ARCHIVE_OK)
@@ -666,15 +698,65 @@
 	archive_entry_set_ctime(entry, file->ctime, 0);
 	archive_entry_set_mtime(entry, file->mtime, 0);
 	archive_entry_set_gid(entry, file->gid);
-	if (file->gname.length > 0)
-		archive_entry_update_gname_utf8(entry, file->gname.s);
+	if (file->gname.length > 0 &&
+	    archive_entry_copy_gname_l(entry, file->gname.s,
+		archive_strlen(&(file->gname)), xar->sconv) != 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,
+		    "Gname cannot be converted from %s to current locale.",
+		    archive_string_conversion_charset_name(xar->sconv));
+		r = ARCHIVE_WARN;
+	}
 	archive_entry_set_uid(entry, file->uid);
-	if (file->uname.length > 0)
-		archive_entry_update_uname_utf8(entry, file->uname.s);
+	if (file->uname.length > 0 &&
+	    archive_entry_copy_uname_l(entry, file->uname.s,
+		archive_strlen(&(file->uname)), xar->sconv) != 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,
+		    "Uname cannot be converted from %s to current locale.",
+		    archive_string_conversion_charset_name(xar->sconv));
+		r = ARCHIVE_WARN;
+	}
 	archive_entry_set_mode(entry, file->mode);
-	archive_entry_update_pathname_utf8(entry, file->pathname.s);
-	if (file->symlink.length > 0)
-		archive_entry_update_symlink_utf8(entry, file->symlink.s);
+	if (archive_entry_copy_pathname_l(entry, file->pathname.s,
+	    archive_strlen(&(file->pathname)), xar->sconv) != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Pathname cannot be converted from %s to current locale.",
+		    archive_string_conversion_charset_name(xar->sconv));
+		r = ARCHIVE_WARN;
+	}
+
+
+	if (file->symlink.length > 0 &&
+	    archive_entry_copy_symlink_l(entry, file->symlink.s,
+		archive_strlen(&(file->symlink)), xar->sconv) != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Linkname cannot be converted from %s to current locale.",
+		    archive_string_conversion_charset_name(xar->sconv));
+		r = ARCHIVE_WARN;
+	}
 	/* Set proper nlink. */
 	if ((file->mode & AE_IFMT) == AE_IFDIR)
 		archive_entry_set_nlink(entry, file->subdirs + 2);
@@ -682,8 +764,7 @@
 		archive_entry_set_nlink(entry, file->nlink);
 	archive_entry_set_size(entry, file->size);
 	if (archive_strlen(&(file->hardlink)) > 0)
-		archive_entry_update_hardlink_utf8(entry,
-			file->hardlink.s);
+		archive_entry_set_hardlink(entry, file->hardlink.s);
 	archive_entry_set_ino64(entry, file->ino64);
 	if (file->has & HAS_DEV)
 		archive_entry_set_dev(entry, file->dev);
@@ -704,7 +785,6 @@
 	/*
 	 * Read extended attributes.
 	 */
-	r = ARCHIVE_OK;
 	xattr = file->xattr_list;
 	while (xattr != NULL) {
 		const void *d;
@@ -754,13 +834,19 @@
 
 static int
 xar_read_data(struct archive_read *a,
-    const void **buff, size_t *size, off_t *offset)
+    const void **buff, size_t *size, int64_t *offset)
 {
 	struct xar *xar;
 	size_t used;
 	int r;
 
 	xar = (struct xar *)(a->format->data);
+
+	if (xar->entry_unconsumed) {
+		__archive_read_consume(a, xar->entry_unconsumed);
+		xar->entry_unconsumed = 0;
+	}
+
 	if (xar->end_of_file || xar->entry_remaining <= 0) {
 		r = ARCHIVE_EOF;
 		goto abort_read_data;
@@ -786,7 +872,7 @@
 	xar->total += *size;
 	xar->offset += used;
 	xar->entry_remaining -= used;
-	__archive_read_consume(a, used);
+	xar->entry_unconsumed = used;
 
 	if (xar->entry_remaining == 0) {
 		if (xar->entry_total != xar->entry_size) {
@@ -819,10 +905,12 @@
 	xar = (struct xar *)(a->format->data);
 	if (xar->end_of_file)
 		return (ARCHIVE_EOF);
-	bytes_skipped = __archive_read_skip(a, xar->entry_remaining);
+	bytes_skipped = __archive_read_consume(a, xar->entry_remaining +
+		xar->entry_unconsumed);
 	if (bytes_skipped < 0)
 		return (ARCHIVE_FATAL);
 	xar->offset += bytes_skipped;
+	xar->entry_unconsumed = 0;
 	return (ARCHIVE_OK);
 }
 
@@ -853,6 +941,7 @@
 		archive_string_free(&(tag->name));
 		free(tag);
 	}
+	free(xar->outbuff);
 	free(xar);
 	a->format->data = NULL;
 	return (r);
@@ -870,7 +959,7 @@
 
 		step = offset - (xar->offset - xar->h_base);
 		if (step > 0) {
-			step = __archive_read_skip(a, step);
+			step = __archive_read_consume(a, step);
 			if (step < 0)
 				return ((int)step);
 			xar->offset += step;
@@ -1011,6 +1100,8 @@
 #if HAVE_TIMEGM
 	/* Use platform timegm() if available. */
 	return (timegm(t));
+#elif HAVE__MKGMTIME64
+	return (_mkgmtime64(t));
 #else
 	/* Else use direct calculation using POSIX assumptions. */
 	/* First, fix up tm_yday based on the year/month/day. */
@@ -1084,8 +1175,9 @@
 	return (t);
 }
 
-static void
-heap_add_entry(struct heap_queue *heap, struct xar_file *file)
+static int
+heap_add_entry(struct archive_read *a,
+    struct heap_queue *heap, struct xar_file *file)
 {
 	uint64_t file_id, parent_id;
 	int hole, parent;
@@ -1098,12 +1190,18 @@
 		if (heap->allocated < 1024)
 			new_size = 1024;
 		/* Overflow might keep us from growing the list. */
-		if (new_size <= heap->allocated)
-			__archive_errx(1, "Out of memory");
+		if (new_size <= heap->allocated) {
+			archive_set_error(&a->archive,
+			    ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
 		new_pending_files = (struct xar_file **)
 		    malloc(new_size * sizeof(new_pending_files[0]));
-		if (new_pending_files == NULL)
-			__archive_errx(1, "Out of memory");
+		if (new_pending_files == NULL) {
+			archive_set_error(&a->archive,
+			    ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
 		memcpy(new_pending_files, heap->files,
 		    heap->allocated * sizeof(new_pending_files[0]));
 		if (heap->files != NULL)
@@ -1123,13 +1221,15 @@
 		parent_id = heap->files[parent]->id;
 		if (file_id >= parent_id) {
 			heap->files[hole] = file;
-			return;
+			return (ARCHIVE_OK);
 		}
-		// Move parent into hole <==> move hole up tree.
+		/* Move parent into hole <==> move hole up tree. */
 		heap->files[hole] = heap->files[parent];
 		hole = parent;
 	}
 	heap->files[0] = file;
+
+	return (ARCHIVE_OK);
 }
 
 static struct xar_file *
@@ -1155,14 +1255,14 @@
 	/*
 	 * Rebalance the heap.
 	 */
-	a = 0; // Starting element and its heap key
+	a = 0; /* Starting element and its heap key */
 	a_id = heap->files[a]->id;
 	for (;;) {
-		b = a + a + 1; // First child
+		b = a + a + 1; /* First child */
 		if (b >= heap->used)
 			return (r);
 		b_id = heap->files[b]->id;
-		c = b + 1; // Use second child if it is smaller.
+		c = b + 1; /* Use second child if it is smaller. */
 		if (c < heap->used) {
 			c_id = heap->files[c]->id;
 			if (c_id < b_id) {
@@ -1179,8 +1279,8 @@
 	}
 }
 
-static void
-add_link(struct xar *xar, struct xar_file *file)
+static int
+add_link(struct archive_read *a, struct xar *xar, struct xar_file *file)
 {
 	struct hdlink *hdlink;
 
@@ -1189,18 +1289,21 @@
 			file->hdnext = hdlink->files;
 			hdlink->cnt++;
 			hdlink->files = file;
-			return;
+			return (ARCHIVE_OK);
 		}
 	}
 	hdlink = malloc(sizeof(*hdlink));
-	if (hdlink == NULL)
-		__archive_errx(1, "No memory for add_link()");
+	if (hdlink == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
 	file->hdnext = NULL;
 	hdlink->id = file->link;
 	hdlink->cnt = 1;
 	hdlink->files = file;
 	hdlink->next = xar->hdlink_list;
 	xar->hdlink_list = hdlink;
+	return (ARCHIVE_OK);
 }
 
 static void
@@ -1362,6 +1465,13 @@
 		break;
 #endif
 #if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+#if LZMA_VERSION_MAJOR >= 5
+/* Effectively disable the limiter. */
+#define LZMA_MEMLIMIT   UINT64_MAX
+#else
+/* NOTE: This needs to check memory size which running system has. */
+#define LZMA_MEMLIMIT   (1U << 30)
+#endif
 	case XZ:
 	case LZMA:
 		if (xar->lzstream_valid) {
@@ -1370,11 +1480,11 @@
 		}
 		if (xar->entry_encoding == XZ)
 			r = lzma_stream_decoder(&(xar->lzstream),
-			    (1U << 30),/* memlimit */
+			    LZMA_MEMLIMIT,/* memlimit */
 			    LZMA_CONCATENATED);
 		else
 			r = lzma_alone_decoder(&(xar->lzstream),
-			    (1U << 30));/* memlimit */
+			    LZMA_MEMLIMIT);/* memlimit */
 		if (r != LZMA_OK) {
 			switch (r) {
 			case LZMA_MEM_ERROR:
@@ -1473,9 +1583,17 @@
 	avail_in = *used;
 	outbuff = (void *)(uintptr_t)*buff;
 	if (outbuff == NULL) {
-		outbuff = xar->buff;
+		if (xar->outbuff == NULL) {
+			xar->outbuff = malloc(OUTBUFF_SIZE);
+			if (xar->outbuff == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Couldn't allocate memory for out buffer");
+				return (ARCHIVE_FATAL);
+			}
+		}
+		outbuff = xar->outbuff;
 		*buff = outbuff;
-		avail_out = sizeof(xar->buff);
+		avail_out = OUTBUFF_SIZE;
 	} else
 		avail_out = *outbytes;
 	switch (xar->rd_encoding) {
@@ -1599,7 +1717,7 @@
 #endif
 	case NONE:
 	default:
-		if (outbuff == xar->buff) {
+		if (outbuff == xar->outbuff) {
 			*buff = b;
 			*used = avail_in;
 			*outbytes = avail_in;
@@ -1674,15 +1792,17 @@
 	list->last = &(list->first);
 }
 
-static void
-file_new(struct xar *xar, struct xmlattr_list *list)
+static int
+file_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list)
 {
 	struct xar_file *file;
 	struct xmlattr *attr;
 
 	file = calloc(1, sizeof(*file));
-	if (file == NULL)
-		__archive_errx(1, "Out of memory");
+	if (file == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
 	file->parent = xar->file;
 	file->mode = 0777 | AE_IFREG;
 	file->atime = time(NULL);
@@ -1694,7 +1814,9 @@
 			file->id = atol10(attr->value, strlen(attr->value));
 	}
 	file->nlink = 1;
-	heap_add_entry(&(xar->file_queue), file);
+	if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	return (ARCHIVE_OK);
 }
 
 static void
@@ -1719,15 +1841,17 @@
 	free(file);
 }
 
-static void
-xattr_new(struct xar *xar, struct xmlattr_list *list)
+static int
+xattr_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list)
 {
 	struct xattr *xattr, **nx;
 	struct xmlattr *attr;
 
 	xattr = calloc(1, sizeof(*xattr));
-	if (xattr == NULL)
-		__archive_errx(1, "Out of memory");
+	if (xattr == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
 	xar->xattr = xattr;
 	for (attr = list->first; attr != NULL; attr = attr->next) {
 		if (strcmp(attr->name, "id") == 0)
@@ -1741,6 +1865,8 @@
 	}
 	xattr->next = *nx;
 	*nx = xattr;
+
+	return (ARCHIVE_OK);
 }
 
 static void
@@ -1796,8 +1922,8 @@
 	return (alg);
 }
 
-static void
-unknowntag_start(struct xar *xar, const char *name)
+static int
+unknowntag_start(struct archive_read *a, struct xar *xar, const char *name)
 {
 	struct unknown_tag *tag;
 
@@ -1805,8 +1931,10 @@
 	fprintf(stderr, "unknowntag_start:%s\n", name);
 #endif
 	tag = malloc(sizeof(*tag));
-	if (tag == NULL)
-		__archive_errx(1, "Out of memory");
+	if (tag == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
 	tag->next = xar->unknowntags;
 	archive_string_init(&(tag->name));
 	archive_strcpy(&(tag->name), name);
@@ -1815,6 +1943,7 @@
 		xar->xmlsts = UNKNOWN;
 	}
 	xar->unknowntags = tag;
+	return (ARCHIVE_OK);
 }
 
 static void
@@ -1837,14 +1966,12 @@
 	}
 }
 
-static void
-xml_start(void *userData, const char *name, struct xmlattr_list *list)
+static int
+xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list)
 {
-	struct archive_read *a;
 	struct xar *xar;
 	struct xmlattr *attr;
 
-	a = (struct archive_read *)userData;
 	xar = (struct xar *)(a->format->data);
 
 #if DEBUG
@@ -1859,13 +1986,15 @@
 		if (strcmp(name, "xar") == 0)
 			xar->xmlsts = XAR;
 		else
-			unknowntag_start(xar, name);
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 		break;
 	case XAR:
 		if (strcmp(name, "toc") == 0)
 			xar->xmlsts = TOC;
 		else
-			unknowntag_start(xar, name);
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 		break;
 	case TOC:
 		if (strcmp(name, "creation-time") == 0)
@@ -1873,11 +2002,13 @@
 		else if (strcmp(name, "checksum") == 0)
 			xar->xmlsts = TOC_CHECKSUM;
 		else if (strcmp(name, "file") == 0) {
-			file_new(xar, list);
+			if (file_new(a, xar, list) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 			xar->xmlsts = TOC_FILE;
 		}
 		else
-			unknowntag_start(xar, name);
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 		break;
 	case TOC_CHECKSUM:
 		if (strcmp(name, "offset") == 0)
@@ -1885,16 +2016,19 @@
 		else if (strcmp(name, "size") == 0)
 			xar->xmlsts = TOC_CHECKSUM_SIZE;
 		else
-			unknowntag_start(xar, name);
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 		break;
 	case TOC_FILE:
 		if (strcmp(name, "file") == 0) {
-			file_new(xar, list);
+			if (file_new(a, xar, list) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 		}
 		else if (strcmp(name, "data") == 0)
 			xar->xmlsts = FILE_DATA;
 		else if (strcmp(name, "ea") == 0) {
-			xattr_new(xar, list);
+			if (xattr_new(a, xar, list) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 			xar->xmlsts = FILE_EA;
 		}
 		else if (strcmp(name, "ctime") == 0)
@@ -1934,7 +2068,9 @@
 					xar->file->link = atol10(attr->value,
 					    strlen(attr->value));
 					if (xar->file->link > 0)
-						add_link(xar, xar->file);
+						if (add_link(a, xar, xar->file) != ARCHIVE_OK) {
+							return (ARCHIVE_FATAL);
+						};
 				}
 			}
 		}
@@ -1954,7 +2090,8 @@
 		else if (strcmp(name, "ext2") == 0)
 			xar->xmlsts = FILE_EXT2;
 		else
-			unknowntag_start(xar, name);
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 		break;
 	case FILE_DATA:
 		if (strcmp(name, "length") == 0)
@@ -1978,7 +2115,8 @@
 		else if (strcmp(name, "content") == 0)
 			xar->xmlsts = FILE_DATA_CONTENT;
 		else
-			unknowntag_start(xar, name);
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 		break;
 	case FILE_DEVICE:
 		if (strcmp(name, "major") == 0)
@@ -1986,10 +2124,12 @@
 		else if (strcmp(name, "minor") == 0)
 			xar->xmlsts = FILE_DEVICE_MINOR;
 		else
-			unknowntag_start(xar, name);
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 		break;
 	case FILE_DATA_CONTENT:
-		unknowntag_start(xar, name);
+		if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
 		break;
 	case FILE_EA:
 		if (strcmp(name, "length") == 0)
@@ -2010,7 +2150,8 @@
 		else if (strcmp(name, "fstype") == 0)
 			xar->xmlsts = FILE_EA_FSTYPE;
 		else
-			unknowntag_start(xar, name);
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 		break;
 	case FILE_ACL:
 		if (strcmp(name, "appleextended") == 0)
@@ -2020,15 +2161,18 @@
 		else if (strcmp(name, "access") == 0)
 			xar->xmlsts = FILE_ACL_ACCESS;
 		else
-			unknowntag_start(xar, name);
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 		break;
 	case FILE_FLAGS:
 		if (!xml_parse_file_flags(xar, name))
-			unknowntag_start(xar, name);
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 		break;
 	case FILE_EXT2:
 		if (!xml_parse_file_ext2(xar, name))
-			unknowntag_start(xar, name);
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
 		break;
 	case TOC_CREATION_TIME:
 	case TOC_CHECKSUM_OFFSET:
@@ -2096,9 +2240,11 @@
 	case FILE_EXT2_TopDir:
 	case FILE_EXT2_Reserved:
 	case UNKNOWN:
-		unknowntag_start(xar, name);
+		if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
 		break;
 	}
+	return (ARCHIVE_OK);
 }
 
 static void
@@ -2470,13 +2616,15 @@
 };
 
 static void
-strappend_base64(struct archive_string *as, const char *s, size_t l)
+strappend_base64(struct xar *xar,
+    struct archive_string *as, const char *s, size_t l)
 {
 	unsigned char buff[256];
 	unsigned char *out;
 	const unsigned char *b;
 	size_t len;
 
+	(void)xar; /* UNUSED */
 	len = 0;
 	out = buff;
 	b = (const unsigned char *)s;
@@ -2530,8 +2678,8 @@
 #if DEBUG
 	{
 		char buff[1024];
-		if (len > (int)sizeof(buff)-1)
-			len = (int)sizeof(buff)-1;
+		if (len > sizeof(buff)-1)
+			len = sizeof(buff)-1;
 		memcpy(buff, s, len);
 		buff[len] = 0;
 		fprintf(stderr, "\tlen=%d:\"%s\"\n", len, buff);
@@ -2558,9 +2706,10 @@
 			archive_strappend_char(&(xar->file->pathname), '/');
 		}
 		xar->file->has |= HAS_PATHNAME;
-		if (xar->base64text)
-			strappend_base64(&(xar->file->pathname), s, len);
-		else
+		if (xar->base64text) {
+			strappend_base64(xar,
+			    &(xar->file->pathname), s, len);
+		} else
 			archive_strncat(&(xar->file->pathname), s, len);
 		break;
 	case FILE_LINK:
@@ -2909,7 +3058,8 @@
 #ifdef HAVE_LIBXML_XMLREADER_H
 
 static int
-xml2_xmlattr_setup(struct xmlattr_list *list, xmlTextReaderPtr reader)
+xml2_xmlattr_setup(struct archive_read *a,
+    struct xmlattr_list *list, xmlTextReaderPtr reader)
 {
 	struct xmlattr *attr;
 	int r;
@@ -2919,16 +3069,22 @@
 	r = xmlTextReaderMoveToFirstAttribute(reader);
 	while (r == 1) {
 		attr = malloc(sizeof*(attr));
-		if (attr == NULL)
-			__archive_errx(1, "Out of memory");
+		if (attr == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
 		attr->name = strdup(
 		    (const char *)xmlTextReaderConstLocalName(reader));
-		if (attr->name == NULL)
-			__archive_errx(1, "Out of memory");
+		if (attr->name == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
 		attr->value = strdup(
 		    (const char *)xmlTextReaderConstValue(reader));
-		if (attr->value == NULL)
-			__archive_errx(1, "Out of memory");
+		if (attr->value == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
 		attr->next = NULL;
 		*list->last = attr;
 		list->last = &(attr->next);
@@ -3020,13 +3176,15 @@
 		switch (type) {
 		case XML_READER_TYPE_ELEMENT:
 			empty = xmlTextReaderIsEmptyElement(reader);
-			r = xml2_xmlattr_setup(&list, reader);
-			if (r == 0) {
-				xml_start(a, name, &list);
-				xmlattr_cleanup(&list);
-				if (empty)
-					xml_end(a, name);
-			}
+			r = xml2_xmlattr_setup(a, &list, reader);
+			if (r != ARCHIVE_OK)
+				return (r);
+			r = xml_start(a, name, &list);
+			xmlattr_cleanup(&list);
+			if (r != ARCHIVE_OK)
+				return (r);
+			if (empty)
+				xml_end(a, name);
 			break;
 		case XML_READER_TYPE_END_ELEMENT:
 			xml_end(a, name);
@@ -3050,52 +3208,64 @@
 
 #elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
 
-static void
-expat_xmlattr_setup(struct xmlattr_list *list, const XML_Char **atts)
+static int
+expat_xmlattr_setup(struct archive_read *a,
+    struct xmlattr_list *list, const XML_Char **atts)
 {
 	struct xmlattr *attr;
+	char *name, *value;
 
 	list->first = NULL;
 	list->last = &(list->first);
 	if (atts == NULL)
-		return;
+		return (ARCHIVE_OK);
 	while (atts[0] != NULL && atts[1] != NULL) {
 		attr = malloc(sizeof*(attr));
-		if (attr == NULL)
-			__archive_errx(1, "Out of memory");
-		attr->name = strdup(atts[0]);
-		if (attr->name == NULL)
-			__archive_errx(1, "Out of memory");
-		attr->value = strdup(atts[1]);
-		if (attr->value == NULL)
-			__archive_errx(1, "Out of memory");
+		name = strdup(atts[0]);
+		value = strdup(atts[1]);
+		if (attr == NULL || name == NULL || value == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
+		attr->name = name;
+		attr->value = value;
 		attr->next = NULL;
 		*list->last = attr;
 		list->last = &(attr->next);
 		atts += 2;
 	}
+	return (ARCHIVE_OK);
 }
 
 static void
 expat_start_cb(void *userData, const XML_Char *name, const XML_Char **atts)
 {
+	struct expat_userData *ud = (struct expat_userData *)userData;
+	struct archive_read *a = ud->archive;
 	struct xmlattr_list list;
+	int r;
 
-	expat_xmlattr_setup(&list, atts);
-	xml_start(userData, (const char *)name, &list);
+	r = expat_xmlattr_setup(a, &list, atts);
+	if (r == ARCHIVE_OK)
+		r = xml_start(a, (const char *)name, &list);
 	xmlattr_cleanup(&list);
+	ud->state = r;
 }
 
 static void
 expat_end_cb(void *userData, const XML_Char *name)
 {
-	xml_end(userData, (const char *)name);
+	struct expat_userData *ud = (struct expat_userData *)userData;
+
+	xml_end(ud->archive, (const char *)name);
 }
 
 static void
 expat_data_cb(void *userData, const XML_Char *s, int len)
 {
-	xml_data(userData, s, len);
+	struct expat_userData *ud = (struct expat_userData *)userData;
+
+	xml_data(ud->archive, s, len);
 }
 
 static int
@@ -3103,6 +3273,10 @@
 {
 	struct xar *xar;
 	XML_Parser parser;
+	struct expat_userData ud;
+
+	ud.state = ARCHIVE_OK;
+	ud.archive = a;
 
 	xar = (struct xar *)(a->format->data);
 
@@ -3113,12 +3287,12 @@
 		    "Couldn't allocate memory for xml parser");
 		return (ARCHIVE_FATAL);
 	}
-	XML_SetUserData(parser, a);
+	XML_SetUserData(parser, &ud);
 	XML_SetElementHandler(parser, expat_start_cb, expat_end_cb);
 	XML_SetCharacterDataHandler(parser, expat_data_cb);
 	xar->xmlsts = INIT;
 
-	while (xar->toc_remaining) {
+	while (xar->toc_remaining && ud.state == ARCHIVE_OK) {
 		enum XML_Status xr;
 		const void *d;
 		size_t outbytes;
@@ -3129,13 +3303,13 @@
 		r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);
 		if (r != ARCHIVE_OK)
 			return (r);
-		__archive_read_consume(a, used);
 		xar->toc_remaining -= used;
 		xar->offset += used;
 		xar->toc_total += outbytes;
 		PRINT_TOC(d, outbytes);
 
 		xr = XML_Parse(parser, d, outbytes, xar->toc_remaining == 0);
+		__archive_read_consume(a, used);
 		if (xr == XML_STATUS_ERROR) {
 			XML_ParserFree(parser);
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -3144,7 +3318,7 @@
 		}
 	}
 	XML_ParserFree(parser);
-	return (ARCHIVE_OK);
+	return (ud.state);
 }
 #endif /* defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) */
 
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_read_support_format_zip.c
--- a/head/contrib/libarchive/libarchive/archive_read_support_format_zip.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_read_support_format_zip.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2004 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,22 +25,21 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_zip.c 228773 2011-12-21 15:18:52Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_read_support_format_zip.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
-#include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
-#include <time.h>
 #ifdef HAVE_ZLIB_H
 #include <zlib.h>
 #endif
 
 #include "archive.h"
 #include "archive_entry.h"
+#include "archive_entry_locale.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
 #include "archive_endian.h"
@@ -48,10 +48,39 @@
 #include "archive_crc32.h"
 #endif
 
+struct zip_entry {
+	int64_t			local_header_offset;
+	int64_t			compressed_size;
+	int64_t			uncompressed_size;
+	int64_t			gid;
+	int64_t			uid;
+	struct archive_entry	*entry;
+	time_t			mtime;
+	time_t			atime;
+	time_t			ctime;
+	uint32_t		crc32;
+	uint16_t		mode;
+	uint16_t		flags;
+	char			compression;
+	char			system;
+};
+
 struct zip {
+	/* Structural information about the archive. */
+	int64_t			central_directory_offset;
+	size_t			central_directory_size;
+	size_t			central_directory_entries;
+	char			have_central_directory;
+
+	/* List of entries (seekable Zip only) */
+	size_t			entries_remaining;
+	struct zip_entry	*zip_entries;
+	struct zip_entry	*entry;
+
+	size_t			unconsumed;
+
 	/* entry_bytes_remaining is the number of bytes we expect. */
 	int64_t			entry_bytes_remaining;
-	int64_t			entry_offset;
 
 	/* These count the number of bytes actually read for the entry. */
 	int64_t			entry_compressed_bytes_read;
@@ -60,27 +89,12 @@
 	/* Running CRC32 of the decompressed data */
 	unsigned long		entry_crc32;
 
-	unsigned		version;
-	unsigned		system;
-	unsigned		flags;
-	unsigned		compression;
-	const char *		compression_name;
-	time_t			mtime;
-	time_t			ctime;
-	time_t			atime;
-	mode_t			mode;
-	uid_t			uid;
-	gid_t			gid;
-
 	/* Flags to mark progress of decompression. */
 	char			decompress_init;
 	char			end_of_entry;
 
-	unsigned long		crc32;
 	ssize_t			filename_length;
 	ssize_t			extra_length;
-	int64_t			uncompressed_size;
-	int64_t			compressed_size;
 
 	unsigned char 		*uncompressed_buffer;
 	size_t 			uncompressed_buffer_size;
@@ -89,65 +103,60 @@
 	char			stream_valid;
 #endif
 
-	struct archive_string	pathname;
 	struct archive_string	extra;
+	struct archive_string_conv *sconv;
+	struct archive_string_conv *sconv_default;
+	struct archive_string_conv *sconv_utf8;
+	int			init_default_conversion;
 	char	format_name[64];
 };
 
 #define ZIP_LENGTH_AT_END	8
+#define ZIP_ENCRYPTED		(1<<0)	
+#define ZIP_STRONG_ENCRYPTED	(1<<6)	
+#define ZIP_UTF8_NAME		(1<<11)	
 
-struct zip_file_header {
-	char	signature[4];
-	char	version[2];
-	char	flags[2];
-	char	compression[2];
-	char	timedate[4];
-	char	crc32[4];
-	char	compressed_size[4];
-	char	uncompressed_size[4];
-	char	filename_length[2];
-	char	extra_length[2];
-};
-
-static const char *compression_names[] = {
-	"uncompressed",
-	"shrinking",
-	"reduced-1",
-	"reduced-2",
-	"reduced-3",
-	"reduced-4",
-	"imploded",
-	"reserved",
-	"deflation"
-};
-
-static int	archive_read_format_zip_bid(struct archive_read *);
+static int	archive_read_format_zip_streamable_bid(struct archive_read *, int);
+static int	archive_read_format_zip_seekable_bid(struct archive_read *, int);
+static int	archive_read_format_zip_options(struct archive_read *,
+		    const char *, const char *);
 static int	archive_read_format_zip_cleanup(struct archive_read *);
 static int	archive_read_format_zip_read_data(struct archive_read *,
-		    const void **, size_t *, off_t *);
+		    const void **, size_t *, int64_t *);
 static int	archive_read_format_zip_read_data_skip(struct archive_read *a);
-static int	archive_read_format_zip_read_header(struct archive_read *,
+static int	archive_read_format_zip_seekable_read_header(struct archive_read *,
 		    struct archive_entry *);
-static int	search_next_signature(struct archive_read *);
+static int	archive_read_format_zip_streamable_read_header(struct archive_read *,
+		    struct archive_entry *);
+#ifdef HAVE_ZLIB_H
 static int	zip_read_data_deflate(struct archive_read *a, const void **buff,
-		    size_t *size, off_t *offset);
+		    size_t *size, int64_t *offset);
+#endif
 static int	zip_read_data_none(struct archive_read *a, const void **buff,
-		    size_t *size, off_t *offset);
-static int	zip_read_file_header(struct archive_read *a,
-		    struct archive_entry *entry, struct zip *zip);
+		    size_t *size, int64_t *offset);
+static int	zip_read_local_file_header(struct archive_read *a,
+    struct archive_entry *entry, struct zip *);
 static time_t	zip_time(const char *);
-static void process_extra(const void* extra, struct zip* zip);
+static const char *compression_name(int compression);
+static void process_extra(const char *, size_t, struct zip_entry *);
+
+int	archive_read_support_format_zip_streamable(struct archive *);
+int	archive_read_support_format_zip_seekable(struct archive *);
 
 int
-archive_read_support_format_zip(struct archive *_a)
+archive_read_support_format_zip_streamable(struct archive *_a)
 {
 	struct archive_read *a = (struct archive_read *)_a;
 	struct zip *zip;
 	int r;
 
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_zip");
+
 	zip = (struct zip *)malloc(sizeof(*zip));
 	if (zip == NULL) {
-		archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data");
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate zip data");
 		return (ARCHIVE_FATAL);
 	}
 	memset(zip, 0, sizeof(*zip));
@@ -155,9 +164,9 @@
 	r = __archive_read_register_format(a,
 	    zip,
 	    "zip",
-	    archive_read_format_zip_bid,
-	    NULL,
-	    archive_read_format_zip_read_header,
+	    archive_read_format_zip_streamable_bid,
+	    archive_read_format_zip_options,
+	    archive_read_format_zip_streamable_read_header,
 	    archive_read_format_zip_read_data,
 	    archive_read_format_zip_read_data_skip,
 	    archive_read_format_zip_cleanup);
@@ -167,13 +176,234 @@
 	return (ARCHIVE_OK);
 }
 
+int
+archive_read_support_format_zip_seekable(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct zip *zip;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_zip_seekable");
+
+	zip = (struct zip *)malloc(sizeof(*zip));
+	if (zip == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate zip data");
+		return (ARCHIVE_FATAL);
+	}
+	memset(zip, 0, sizeof(*zip));
+
+	r = __archive_read_register_format(a,
+	    zip,
+	    "zip",
+	    archive_read_format_zip_seekable_bid,
+	    archive_read_format_zip_options,
+	    archive_read_format_zip_seekable_read_header,
+	    archive_read_format_zip_read_data,
+	    archive_read_format_zip_read_data_skip,
+	    archive_read_format_zip_cleanup);
+
+	if (r != ARCHIVE_OK)
+		free(zip);
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_support_format_zip(struct archive *a)
+{
+	int r;
+	r = archive_read_support_format_zip_streamable(a);
+	if (r != ARCHIVE_OK)
+		return r;
+	return (archive_read_support_format_zip_seekable(a));
+}
+
+/*
+ * TODO: This is a performance sink because it forces the read core to
+ * drop buffered data from the start of file, which will then have to
+ * be re-read again if this bidder loses.
+ *
+ * We workaround this a little by passing in the best bid so far so
+ * that later bidders can do nothing if they know they'll never
+ * outbid.  But we can certainly do better...
+ */
+static int
+archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
+{
+	struct zip *zip = (struct zip *)a->format->data;
+	int64_t filesize;
+	const char *p;
+
+	/* If someone has already bid more than 32, then avoid
+	   trashing the look-ahead buffers with a seek. */
+	if (best_bid > 32)
+		return (-1);
+
+	filesize = __archive_read_seek(a, -22, SEEK_END);
+	/* If we can't seek, then we can't bid. */
+	if (filesize <= 0)
+		return 0;
+
+	/* TODO: More robust search for end of central directory record. */
+	if ((p = __archive_read_ahead(a, 22, NULL)) == NULL)
+		return 0;
+	/* First four bytes are signature for end of central directory
+	   record.  Four zero bytes ensure this isn't a multi-volume
+	   Zip file (which we don't yet support). */
+	if (memcmp(p, "PK\005\006\000\000\000\000", 8) != 0)
+		return 0;
+
+	/* Since we've already done the hard work of finding the
+	   end of central directory record, let's save the important
+	   information. */
+	zip->central_directory_entries = archive_le16dec(p + 10);
+	zip->central_directory_size = archive_le32dec(p + 12);
+	zip->central_directory_offset = archive_le32dec(p + 16);
+
+	/* Just one volume, so central dir must all be on this volume. */
+	if (zip->central_directory_entries != archive_le16dec(p + 8))
+		return 0;
+	/* Central directory can't extend beyond end of this file. */
+	if (zip->central_directory_offset + (int64_t)zip->central_directory_size > filesize)
+		return 0;
+
+	/* This is just a tiny bit higher than the maximum returned by
+	   the streaming Zip bidder.  This ensures that the more accurate
+	   seeking Zip parser wins whenever seek is available. */
+	return 32;
+}
 
 static int
-archive_read_format_zip_bid(struct archive_read *a)
+slurp_central_directory(struct archive_read *a, struct zip *zip)
+{
+	unsigned i;
+
+	__archive_read_seek(a, zip->central_directory_offset, SEEK_SET);
+
+	zip->zip_entries = calloc(zip->central_directory_entries, sizeof(struct zip_entry));
+	for (i = 0; i < zip->central_directory_entries; ++i) {
+		struct zip_entry *zip_entry = &zip->zip_entries[i];
+		size_t filename_length, extra_length, comment_length;
+		uint32_t external_attributes;
+		const char *p;
+
+		if ((p = __archive_read_ahead(a, 46, NULL)) == NULL)
+			return ARCHIVE_FATAL;
+		if (memcmp(p, "PK\001\002", 4) != 0) {
+			archive_set_error(&a->archive,
+			    -1, "Invalid central directory signature");
+			return ARCHIVE_FATAL;
+		}
+		zip->have_central_directory = 1;
+		/* version = p[4]; */
+		zip_entry->system = p[5];
+		/* version_required = archive_le16dec(p + 6); */
+		zip_entry->flags = archive_le16dec(p + 8);
+		zip_entry->compression = archive_le16dec(p + 10);
+		zip_entry->mtime = zip_time(p + 12);
+		zip_entry->crc32 = archive_le32dec(p + 16);
+		zip_entry->compressed_size = archive_le32dec(p + 20);
+		zip_entry->uncompressed_size = archive_le32dec(p + 24);
+		filename_length = archive_le16dec(p + 28);
+		extra_length = archive_le16dec(p + 30);
+		comment_length = archive_le16dec(p + 32);
+		/* disk_start = archive_le16dec(p + 34); */ /* Better be zero. */
+		/* internal_attributes = archive_le16dec(p + 36); */ /* text bit */
+		external_attributes = archive_le32dec(p + 38);
+		zip_entry->local_header_offset = archive_le32dec(p + 42);
+
+		/* If we can't guess the mode, leave it zero here;
+		   when we read the local file header we might get
+		   more information. */
+		zip_entry->mode = 0;
+		if (zip_entry->system == 3) {
+			zip_entry->mode = external_attributes >> 16;
+		}
+
+		/* We don't read the filename until we get to the
+		   local file header.  Reading it here would speed up
+		   table-of-contents operations (removing the need to
+		   find and read local file header to get the
+		   filename) at the cost of requiring a lot of extra
+		   space. */
+		/* We don't read the extra block here.  We assume it
+		   will be duplicated at the local file header. */
+		__archive_read_consume(a,
+		    46 + filename_length + extra_length + comment_length);
+	}
+
+	/* TODO: Sort zip entries by file offset so that we
+	   can optimize get_next_header() to use skip instead of
+	   seek. */
+
+	return ARCHIVE_OK;
+}
+
+static int
+archive_read_format_zip_seekable_read_header(struct archive_read *a,
+	struct archive_entry *entry)
+{
+	struct zip *zip = (struct zip *)a->format->data;
+	int r;
+
+	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
+	if (a->archive.archive_format_name == NULL)
+		a->archive.archive_format_name = "ZIP";
+
+	if (zip->zip_entries == NULL) {
+		r = slurp_central_directory(a, zip);
+		zip->entries_remaining = zip->central_directory_entries;
+		if (r != ARCHIVE_OK)
+			return r;
+		zip->entry = zip->zip_entries;
+	} else {
+		++zip->entry;
+	}
+
+	if (zip->entries_remaining <= 0)
+		return ARCHIVE_EOF;
+	--zip->entries_remaining;
+
+	/* TODO: If entries are sorted by offset within the file, we
+	   should be able to skip here instead of seeking.  Skipping is
+	   typically faster (easier for I/O layer to optimize). */
+	__archive_read_seek(a, zip->entry->local_header_offset, SEEK_SET);
+	zip->unconsumed = 0;
+	r = zip_read_local_file_header(a, entry, zip);
+	if (r != ARCHIVE_OK)
+		return r;
+	if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) {
+		const void *p;
+		size_t linkname_length = archive_entry_size(entry);
+
+		archive_entry_set_size(entry, 0);
+		p = __archive_read_ahead(a, linkname_length, NULL);
+		if (p == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Truncated Zip file");
+			return ARCHIVE_FATAL;
+		}
+
+		if (archive_entry_copy_symlink_l(entry, p, linkname_length,
+		    NULL) != 0) {
+			/* NOTE: If the last argument is NULL, this will
+			 * fail only by memeory allocation failure. */
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Symlink");
+			return (ARCHIVE_FATAL);
+		}
+		/* TODO: handle character-set issues? */
+	}
+	return ARCHIVE_OK;
+}
+
+static int
+archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid)
 {
 	const char *p;
-	const void *buff;
-	ssize_t bytes_avail, offset;
+
+	(void)best_bid; /* UNUSED */
 
 	if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)
 		return (-1);
@@ -192,321 +422,310 @@
 			return (30);
 	}
 
-	/*
-	 * Attempt to handle self-extracting archives
-	 * by noting a PE header and searching forward
-	 * up to 128k for a 'PK\003\004' marker.
-	 */
-	if (p[0] == 'M' && p[1] == 'Z') {
-		/*
-		 * TODO: Optimize by initializing 'offset' to an
-		 * estimate of the likely start of the archive data
-		 * based on values in the PE header.  Note that we
-		 * don't need to be exact, but we mustn't skip too
-		 * far.  The search below will compensate if we
-		 * undershoot.
-		 */
-		offset = 0;
-		while (offset < 124000) {
-			/* Get 4k of data beyond where we stopped. */
-			buff = __archive_read_ahead(a, offset + 4096,
-			    &bytes_avail);
-			if (buff == NULL)
-				break;
-			p = (const char *)buff + offset;
-			while (p + 9 < (const char *)buff + bytes_avail) {
-				if (p[0] == 'P' && p[1] == 'K' /* signature */
-				    && p[2] == 3 && p[3] == 4 /* File entry */
-				    && p[8] == 8 /* compression == deflate */
-				    && p[9] == 0 /* High byte of compression */
-					)
-				{
-					return (30);
-				}
-				++p;
-			}
-			offset = p - (const char *)buff;
-		}
-	}
+	/* TODO: It's worth looking ahead a little bit for a valid
+	 * PK signature.  In particular, that would make it possible
+	 * to read some UUEncoded SFX files or SFX files coming from
+	 * a network socket. */
 
 	return (0);
 }
 
-/*
- * Search forward for a "PK\003\004" file header.  This handles the
- * case of self-extracting archives, where there is an executable
- * prepended to the ZIP archive.
- */
 static int
-skip_sfx(struct archive_read *a)
+archive_read_format_zip_options(struct archive_read *a,
+    const char *key, const char *val)
 {
-	const void *h;
-	const char *p, *q;
-	size_t skip;
-	ssize_t bytes;
+	struct zip *zip;
+	int ret = ARCHIVE_FAILED;
 
-	/*
-	 * TODO: We should be able to skip forward by a bunch
-	 * by lifting some values from the PE header.  We don't
-	 * need to be exact (we're still going to search forward
-	 * to find the header), but it will speed things up and
-	 * reduce the chance of a false positive.
-	 */
-	for (;;) {
-		h = __archive_read_ahead(a, 4, &bytes);
-		if (bytes < 4)
-			return (ARCHIVE_FATAL);
-		p = h;
-		q = p + bytes;
+	zip = (struct zip *)(a->format->data);
+	if (strcmp(key, "compat-2x")  == 0) {
+		/* Handle filnames as libarchive 2.x */
+		zip->init_default_conversion = (val != NULL) ? 1 : 0;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "zip: hdrcharset option needs a character-set name");
+		else {
+			zip->sconv = archive_string_conversion_from_charset(
+			    &a->archive, val, 0);
+			if (zip->sconv != NULL) {
+				if (strcmp(val, "UTF-8") == 0)
+					zip->sconv_utf8 = zip->sconv;
+				ret = ARCHIVE_OK;
+			} else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
 
-		/*
-		 * Scan ahead until we find something that looks
-		 * like the zip header.
-		 */
-		while (p + 4 < q) {
-			switch (p[3]) {
-			case '\004':
-				/* TODO: Additional verification here. */
-				if (memcmp("PK\003\004", p, 4) == 0) {
-					skip = p - (const char *)h;
-					__archive_read_consume(a, skip);
-					return (ARCHIVE_OK);
-				}
-				p += 4;
-				break;
-			case '\003': p += 1; break;
-			case 'K': p += 2; break;
-			case 'P': p += 3; break;
-			default: p += 4; break;
-			}
-		}
-		skip = p - (const char *)h;
-		__archive_read_consume(a, skip);
-	}
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
 }
 
 static int
-archive_read_format_zip_read_header(struct archive_read *a,
+archive_read_format_zip_streamable_read_header(struct archive_read *a,
     struct archive_entry *entry)
 {
-	const void *h;
-	const char *signature;
 	struct zip *zip;
-	int r = ARCHIVE_OK, r1;
 
 	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
 	if (a->archive.archive_format_name == NULL)
 		a->archive.archive_format_name = "ZIP";
 
 	zip = (struct zip *)(a->format->data);
+
+	/* Make sure we have a zip_entry structure to use. */
+	if (zip->zip_entries == NULL) {
+		zip->zip_entries = malloc(sizeof(struct zip_entry));
+		if (zip->zip_entries == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Out  of memory");
+			return ARCHIVE_FATAL;
+		}
+	}
+	zip->entry = zip->zip_entries;
+	memset(zip->entry, 0, sizeof(struct zip_entry));
+
+	/* Search ahead for the next local file header. */
+	__archive_read_consume(a, zip->unconsumed);
+	zip->unconsumed = 0;
+	for (;;) {
+		int64_t skipped = 0;
+		const char *p, *end;
+		ssize_t bytes;
+
+		p = __archive_read_ahead(a, 4, &bytes);
+		if (p == NULL)
+			return (ARCHIVE_FATAL);
+		end = p + bytes;
+
+		while (p + 4 <= end) {
+			if (p[0] == 'P' && p[1] == 'K') {
+				if (p[2] == '\001' && p[3] == '\002')
+					/* Beginning of central directory. */
+					return (ARCHIVE_EOF);
+
+				if (p[2] == '\003' && p[3] == '\004') {
+					/* Regular file entry. */
+					__archive_read_consume(a, skipped);
+					return zip_read_local_file_header(a, entry, zip);
+				}
+
+				if (p[2] == '\005' && p[3] == '\006')
+					/* End of central directory. */
+					return (ARCHIVE_EOF);
+			}
+			++p;
+			++skipped;
+		}
+		__archive_read_consume(a, skipped);
+	}
+}
+
+/*
+ * Assumes file pointer is at beginning of local file header.
+ */
+static int
+zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
+    struct zip *zip)
+{
+	const char *p;
+	const void *h;
+	const wchar_t *wp;
+	const char *cp;
+	size_t len, filename_length, extra_length;
+	struct archive_string_conv *sconv;
+	struct zip_entry *zip_entry = zip->entry;
+	uint32_t local_crc32;
+	int64_t compressed_size, uncompressed_size;
+	int ret = ARCHIVE_OK;
+	char version;
+
 	zip->decompress_init = 0;
 	zip->end_of_entry = 0;
 	zip->entry_uncompressed_bytes_read = 0;
 	zip->entry_compressed_bytes_read = 0;
 	zip->entry_crc32 = crc32(0, NULL, 0);
-	if ((h = __archive_read_ahead(a, 4, NULL)) == NULL)
-		return (ARCHIVE_FATAL);
 
-	signature = (const char *)h;
-	if (signature[0] == 'M' && signature[1] == 'Z') {
-		/* This is an executable?  Must be self-extracting... */
-		r = skip_sfx(a);
-		if (r < ARCHIVE_WARN)
-			return (r);
-		if ((h = __archive_read_ahead(a, 4, NULL)) == NULL)
-			return (ARCHIVE_FATAL);
-		signature = (const char *)h;
+	/* Setup default conversion. */
+	if (zip->sconv == NULL && !zip->init_default_conversion) {
+		zip->sconv_default =
+		    archive_string_default_conversion_for_read(&(a->archive));
+		zip->init_default_conversion = 1;
 	}
 
-	/* If we don't see a PK signature here, scan forward. */
-	if (signature[0] != 'P' || signature[1] != 'K') {
-		r = search_next_signature(a);
-		if (r != ARCHIVE_OK) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Bad ZIP file");
-			return (ARCHIVE_FATAL);
-		}
-		if ((h = __archive_read_ahead(a, 4, NULL)) == NULL)
-			return (ARCHIVE_FATAL);
-		signature = (const char *)h;
-	}
-
-	/*
-	 * "PK00" signature is used for "split" archives that
-	 * only have a single segment.  This means we can just
-	 * skip the PK00; the first real file header should follow.
-	 */
-	if (signature[2] == '0' && signature[3] == '0') {
-		__archive_read_consume(a, 4);
-		if ((h = __archive_read_ahead(a, 4, NULL)) == NULL)
-			return (ARCHIVE_FATAL);
-		signature = (const char *)h;
-		if (signature[0] != 'P' || signature[1] != 'K') {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Bad ZIP file");
-			return (ARCHIVE_FATAL);
-		}
-	}
-
-	if (signature[2] == '\001' && signature[3] == '\002') {
-		/* Beginning of central directory. */
-		return (ARCHIVE_EOF);
-	}
-
-	if (signature[2] == '\003' && signature[3] == '\004') {
-		/* Regular file entry. */
-		r1 = zip_read_file_header(a, entry, zip);
-		if (r1 != ARCHIVE_OK)
-			return (r1);
-		return (r);
-	}
-
-	if (signature[2] == '\005' && signature[3] == '\006') {
-		/* End-of-archive record. */
-		return (ARCHIVE_EOF);
-	}
-
-	if (signature[2] == '\007' && signature[3] == '\010') {
-		/*
-		 * We should never encounter this record here;
-		 * see ZIP_LENGTH_AT_END handling below for details.
-		 */
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Bad ZIP file: Unexpected end-of-entry record");
-		return (ARCHIVE_FATAL);
-	}
-
-	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-	    "Damaged ZIP file or unsupported format variant (%d,%d)",
-	    signature[2], signature[3]);
-	return (ARCHIVE_FATAL);
-}
-
-static int
-search_next_signature(struct archive_read *a)
-{
-	const void *h;
-	const char *p, *q;
-	size_t skip;
-	ssize_t bytes;
-	int64_t skipped = 0;
-
-	for (;;) {
-		h = __archive_read_ahead(a, 4, &bytes);
-		if (h == NULL)
-			return (ARCHIVE_FATAL);
-		p = h;
-		q = p + bytes;
-
-		while (p + 4 <= q) {
-			if (p[0] == 'P' && p[1] == 'K') {
-				if ((p[2] == '\001' && p[3] == '\002')
-				    || (p[2] == '\003' && p[3] == '\004')
-				    || (p[2] == '\005' && p[3] == '\006')
-				    || (p[2] == '\007' && p[3] == '\010')
-				    || (p[2] == '0' && p[3] == '0')) {
-					skip = p - (const char *)h;
-					__archive_read_consume(a, skip);
-					return (ARCHIVE_OK);
-				}
-			}
-			++p;
-		}
-		skip = p - (const char *)h;
-		__archive_read_consume(a, skip);
-		skipped += skip;
-	}
-}
-
-static int
-zip_read_file_header(struct archive_read *a, struct archive_entry *entry,
-    struct zip *zip)
-{
-	const struct zip_file_header *p;
-	const void *h;
-
-	if ((p = __archive_read_ahead(a, sizeof *p, NULL)) == NULL) {
+	if ((p = __archive_read_ahead(a, 30, NULL)) == NULL) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated ZIP file header");
 		return (ARCHIVE_FATAL);
 	}
 
-	zip->version = p->version[0];
-	zip->system = p->version[1];
-	zip->flags = archive_le16dec(p->flags);
-	zip->compression = archive_le16dec(p->compression);
-	if (zip->compression <
-	    sizeof(compression_names)/sizeof(compression_names[0]))
-		zip->compression_name = compression_names[zip->compression];
-	else
-		zip->compression_name = "??";
-	zip->mtime = zip_time(p->timedate);
-	zip->ctime = 0;
-	zip->atime = 0;
-	zip->mode = 0;
-	zip->uid = 0;
-	zip->gid = 0;
-	zip->crc32 = archive_le32dec(p->crc32);
-	zip->filename_length = archive_le16dec(p->filename_length);
-	zip->extra_length = archive_le16dec(p->extra_length);
-	zip->uncompressed_size = archive_le32dec(p->uncompressed_size);
-	zip->compressed_size = archive_le32dec(p->compressed_size);
+	if (memcmp(p, "PK\003\004", 4) != 0) {
+		archive_set_error(&a->archive, -1, "Damaged Zip archive");
+		return ARCHIVE_FATAL;
+	}
+	version = p[4];
+	zip_entry->system = p[5];
+	zip_entry->flags = archive_le16dec(p + 6);
+	zip_entry->compression = archive_le16dec(p + 8);
+	zip_entry->mtime = zip_time(p + 10);
+	local_crc32 = archive_le32dec(p + 14);
+	compressed_size = archive_le32dec(p + 18);
+	uncompressed_size = archive_le32dec(p + 22);
+	filename_length = archive_le16dec(p + 26);
+	extra_length = archive_le16dec(p + 28);
 
-	__archive_read_consume(a, sizeof(struct zip_file_header));
+	__archive_read_consume(a, 30);
 
+	if (zip->have_central_directory) {
+		/* If we read the central dir entry, we must have size information
+		   as well, so ignore the length-at-end flag. */
+		zip_entry->flags &= ~ZIP_LENGTH_AT_END;
+		/* If we have values from both the local file header
+		   and the central directory, warn about mismatches
+		   which might indicate a damaged file.  But some
+		   writers always put zero in the local header; don't
+		   bother warning about that. */
+		if (local_crc32 != 0 && local_crc32 != zip_entry->crc32) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Inconsistent CRC32 values");
+			ret = ARCHIVE_WARN;
+		}
+		if (compressed_size != 0
+		    && compressed_size != zip_entry->compressed_size) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Inconsistent compressed size");
+			ret = ARCHIVE_WARN;
+		}
+		if (uncompressed_size != 0
+		    && uncompressed_size != zip_entry->uncompressed_size) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Inconsistent uncompressed size");
+			ret = ARCHIVE_WARN;
+		}
+	} else {
+		/* If we don't have the CD info, use whatever we do have. */
+		zip_entry->crc32 = local_crc32;
+		zip_entry->compressed_size = compressed_size;
+		zip_entry->uncompressed_size = uncompressed_size;
+	}
 
 	/* Read the filename. */
-	if ((h = __archive_read_ahead(a, zip->filename_length, NULL)) == NULL) {
+	if ((h = __archive_read_ahead(a, filename_length, NULL)) == NULL) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated ZIP file header");
 		return (ARCHIVE_FATAL);
 	}
-	if (archive_string_ensure(&zip->pathname, zip->filename_length) == NULL)
-		__archive_errx(1, "Out of memory");
-	archive_strncpy(&zip->pathname, h, zip->filename_length);
-	__archive_read_consume(a, zip->filename_length);
-	archive_entry_set_pathname(entry, zip->pathname.s);
+	if (zip_entry->flags & ZIP_UTF8_NAME) {
+		/* The filename is stored to be UTF-8. */
+		if (zip->sconv_utf8 == NULL) {
+			zip->sconv_utf8 =
+			    archive_string_conversion_from_charset(
+				&a->archive, "UTF-8", 1);
+			if (zip->sconv_utf8 == NULL)
+				return (ARCHIVE_FATAL);
+		}
+		sconv = zip->sconv_utf8;
+	} else if (zip->sconv != NULL)
+		sconv = zip->sconv;
+	else
+		sconv = zip->sconv_default;
 
-	if (zip->pathname.s[archive_strlen(&zip->pathname) - 1] == '/')
-		zip->mode = AE_IFDIR | 0777;
-	else
-		zip->mode = AE_IFREG | 0777;
+	if (archive_entry_copy_pathname_l(entry,
+	    h, filename_length, sconv) != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Pathname cannot be converted "
+		    "from %s to current locale.",
+		    archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+	}
+	__archive_read_consume(a, filename_length);
+
+	if (zip_entry->mode == 0) {
+		/* Especially in streaming mode, we can end up
+		   here without having seen any mode information.
+		   Guess from the filename. */
+		wp = archive_entry_pathname_w(entry);
+		if (wp != NULL) {
+			len = wcslen(wp);
+			if (len > 0 && wp[len - 1] == L'/')
+				zip_entry->mode = AE_IFDIR | 0777;
+			else
+				zip_entry->mode = AE_IFREG | 0777;
+		} else {
+			cp = archive_entry_pathname(entry);
+			len = (cp != NULL)?strlen(cp):0;
+			if (len > 0 && cp[len - 1] == '/')
+				zip_entry->mode = AE_IFDIR | 0777;
+			else
+				zip_entry->mode = AE_IFREG | 0777;
+		}
+	}
 
 	/* Read the extra data. */
-	if ((h = __archive_read_ahead(a, zip->extra_length, NULL)) == NULL) {
+	if ((h = __archive_read_ahead(a, extra_length, NULL)) == NULL) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated ZIP file header");
 		return (ARCHIVE_FATAL);
 	}
-	process_extra(h, zip);
-	__archive_read_consume(a, zip->extra_length);
+	process_extra(h, extra_length, zip_entry);
+	__archive_read_consume(a, extra_length);
 
 	/* Populate some additional entry fields: */
-	archive_entry_set_mode(entry, zip->mode);
-	archive_entry_set_uid(entry, zip->uid);
-	archive_entry_set_gid(entry, zip->gid);
-	archive_entry_set_mtime(entry, zip->mtime, 0);
-	archive_entry_set_ctime(entry, zip->ctime, 0);
-	archive_entry_set_atime(entry, zip->atime, 0);
+	archive_entry_set_mode(entry, zip_entry->mode);
+	archive_entry_set_uid(entry, zip_entry->uid);
+	archive_entry_set_gid(entry, zip_entry->gid);
+	archive_entry_set_mtime(entry, zip_entry->mtime, 0);
+	archive_entry_set_ctime(entry, zip_entry->ctime, 0);
+	archive_entry_set_atime(entry, zip_entry->atime, 0);
 	/* Set the size only if it's meaningful. */
-	if (0 == (zip->flags & ZIP_LENGTH_AT_END))
-		archive_entry_set_size(entry, zip->uncompressed_size);
+	if (0 == (zip_entry->flags & ZIP_LENGTH_AT_END))
+		archive_entry_set_size(entry, zip_entry->uncompressed_size);
 
-	zip->entry_bytes_remaining = zip->compressed_size;
-	zip->entry_offset = 0;
+	zip->entry_bytes_remaining = zip_entry->compressed_size;
 
 	/* If there's no body, force read_data() to return EOF immediately. */
-	if (0 == (zip->flags & ZIP_LENGTH_AT_END)
+	if (0 == (zip_entry->flags & ZIP_LENGTH_AT_END)
 	    && zip->entry_bytes_remaining < 1)
 		zip->end_of_entry = 1;
 
 	/* Set up a more descriptive format name. */
 	sprintf(zip->format_name, "ZIP %d.%d (%s)",
-	    zip->version / 10, zip->version % 10,
-	    zip->compression_name);
+	    version / 10, version % 10,
+	    compression_name(zip->entry->compression));
 	a->archive.archive_format_name = zip->format_name;
 
-	return (ARCHIVE_OK);
+	return (ret);
+}
+
+static const char *
+compression_name(int compression)
+{
+	static const char *compression_names[] = {
+		"uncompressed",
+		"shrinking",
+		"reduced-1",
+		"reduced-2",
+		"reduced-3",
+		"reduced-4",
+		"imploded",
+		"reserved",
+		"deflation"
+	};
+
+	if (0 <= compression && compression <
+	    (int)(sizeof(compression_names)/sizeof(compression_names[0])))
+		return compression_names[compression];
+	else
+		return "??";
 }
 
 /* Convert an MSDOS-style date/time into Unix-style time. */
@@ -532,52 +751,49 @@
 
 static int
 archive_read_format_zip_read_data(struct archive_read *a,
-    const void **buff, size_t *size, off_t *offset)
+    const void **buff, size_t *size, int64_t *offset)
 {
 	int r;
-	struct zip *zip;
+	struct zip *zip = (struct zip *)(a->format->data);
 
-	zip = (struct zip *)(a->format->data);
+	*offset = zip->entry_uncompressed_bytes_read;
+	*size = 0;
+	*buff = NULL;
 
-	/*
-	 * If we hit end-of-entry last time, clean up and return
-	 * ARCHIVE_EOF this time.
-	 */
-	if (zip->end_of_entry) {
-		*offset = zip->entry_uncompressed_bytes_read;
-		*size = 0;
-		*buff = NULL;
+	/* If we hit end-of-entry last time, return ARCHIVE_EOF. */
+	if (zip->end_of_entry)
 		return (ARCHIVE_EOF);
+
+	/* Return EOF immediately if this is a non-regular file. */
+	if (AE_IFREG != (zip->entry->mode & AE_IFMT))
+		return (ARCHIVE_EOF);
+
+	if (zip->entry->flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Encrypted file is unsupported");
+		return (ARCHIVE_FAILED);
 	}
 
-	switch(zip->compression) {
+	__archive_read_consume(a, zip->unconsumed);
+	zip->unconsumed = 0;
+
+	switch(zip->entry->compression) {
 	case 0:  /* No compression. */
 		r =  zip_read_data_none(a, buff, size, offset);
 		break;
+#ifdef HAVE_ZLIB_H
 	case 8: /* Deflate compression. */
 		r =  zip_read_data_deflate(a, buff, size, offset);
 		break;
+#endif
 	default: /* Unsupported compression. */
-		*buff = NULL;
-		*size = 0;
-		*offset = 0;
 		/* Return a warning. */
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Unsupported ZIP compression method (%s)",
-		    zip->compression_name);
-		if (zip->flags & ZIP_LENGTH_AT_END) {
-			/*
-			 * ZIP_LENGTH_AT_END requires us to
-			 * decompress the entry in order to
-			 * skip it, but we don't know this
-			 * compression method, so we give up.
-			 */
-			r = ARCHIVE_FATAL;
-		} else {
-			/* We can't decompress this entry, but we will
-			 * be able to skip() it and try the next entry. */
-			r = ARCHIVE_WARN;
-		}
+		    compression_name(zip->entry->compression));
+		/* We can't decompress this entry, but we will
+		 * be able to skip() it and try the next entry. */
+		return (ARCHIVE_FAILED);
 		break;
 	}
 	if (r != ARCHIVE_OK)
@@ -587,116 +803,157 @@
 		zip->entry_crc32 = crc32(zip->entry_crc32, *buff, *size);
 	/* If we hit the end, swallow any end-of-data marker. */
 	if (zip->end_of_entry) {
-		if (zip->flags & ZIP_LENGTH_AT_END) {
-			const char *p;
-
-			if ((p = __archive_read_ahead(a, 16, NULL)) == NULL) {
-				archive_set_error(&a->archive,
-				    ARCHIVE_ERRNO_FILE_FORMAT,
-				    "Truncated ZIP end-of-file record");
-				return (ARCHIVE_FATAL);
-			}
-			zip->crc32 = archive_le32dec(p + 4);
-			zip->compressed_size = archive_le32dec(p + 8);
-			zip->uncompressed_size = archive_le32dec(p + 12);
-			__archive_read_consume(a, 16);
-		}
 		/* Check file size, CRC against these values. */
-		if (zip->compressed_size != zip->entry_compressed_bytes_read) {
+		if (zip->entry->compressed_size != zip->entry_compressed_bytes_read) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-			    "ZIP compressed data is wrong size");
+			    "ZIP compressed data is wrong size (read %jd, expected %jd)",
+			    (intmax_t)zip->entry_compressed_bytes_read,
+			    (intmax_t)zip->entry->compressed_size);
 			return (ARCHIVE_WARN);
 		}
-		/* Size field only stores the lower 32 bits of the actual size. */
-		if ((zip->uncompressed_size & UINT32_MAX)
+		/* Size field only stores the lower 32 bits of the actual
+		 * size. */
+		if ((zip->entry->uncompressed_size & UINT32_MAX)
 		    != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-			    "ZIP uncompressed data is wrong size");
+			    "ZIP uncompressed data is wrong size (read %jd, expected %jd)",
+			    (intmax_t)zip->entry_uncompressed_bytes_read,
+			    (intmax_t)zip->entry->uncompressed_size);
 			return (ARCHIVE_WARN);
 		}
 		/* Check computed CRC against header */
-		if (zip->crc32 != zip->entry_crc32) {
+		if (zip->entry->crc32 != zip->entry_crc32) {
 			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 			    "ZIP bad CRC: 0x%lx should be 0x%lx",
-			    zip->entry_crc32, zip->crc32);
+			    (unsigned long)zip->entry_crc32,
+			    (unsigned long)zip->entry->crc32);
 			return (ARCHIVE_WARN);
 		}
 	}
 
-	/* Return EOF immediately if this is a non-regular file. */
-	if (AE_IFREG != (zip->mode & AE_IFMT))
-		return (ARCHIVE_EOF);
 	return (ARCHIVE_OK);
 }
 
 /*
- * Read "uncompressed" data.  According to the current specification,
- * if ZIP_LENGTH_AT_END is specified, then the size fields in the
- * initial file header are supposed to be set to zero.  This would, of
- * course, make it impossible for us to read the archive, since we
- * couldn't determine the end of the file data.  Info-ZIP seems to
- * include the real size fields both before and after the data in this
- * case (the CRC only appears afterwards), so this works as you would
- * expect.
+ * Read "uncompressed" data.  There are three cases:
+ *  1) We know the size of the data.  This is always true for the
+ * seeking reader (we've examined the Central Directory already).
+ *  2) ZIP_LENGTH_AT_END was set, but only the CRC was deferred.
+ * Info-ZIP seems to do this; we know the size but have to grab
+ * the CRC from the data descriptor afterwards.
+ *  3) We're streaming and ZIP_LENGTH_AT_END was specified and
+ * we have no size information.  In this case, we can do pretty
+ * well by watching for the data descriptor record.  The data
+ * descriptor is 16 bytes and includes a computed CRC that should
+ * provide a strong check.
+ *
+ * TODO: Technically, the PK\007\010 signature is optional.
+ * In the original spec, the data descriptor contained CRC
+ * and size fields but had no leading signature.  In practice,
+ * newer writers seem to provide the signature pretty consistently,
+ * but we might need to do something more complex here if
+ * we want to handle older archives that lack that signature.
  *
  * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
  * zip->end_of_entry if it consumes all of the data.
  */
 static int
-zip_read_data_none(struct archive_read *a, const void **buff,
-    size_t *size, off_t *offset)
+zip_read_data_none(struct archive_read *a, const void **_buff,
+    size_t *size, int64_t *offset)
 {
 	struct zip *zip;
+	const char *buff;
 	ssize_t bytes_avail;
 
+	(void)offset; /* UNUSED */
+
 	zip = (struct zip *)(a->format->data);
 
-	if (zip->entry_bytes_remaining == 0) {
-		*buff = NULL;
-		*size = 0;
-		*offset = zip->entry_offset;
-		zip->end_of_entry = 1;
-		return (ARCHIVE_OK);
+	if (zip->entry->flags & ZIP_LENGTH_AT_END) {
+		const char *p;
+
+		/* Grab at least 16 bytes. */
+		buff = __archive_read_ahead(a, 16, &bytes_avail);
+		if (bytes_avail < 16) {
+			/* Zip archives have end-of-archive markers
+			   that are longer than this, so a failure to get at
+			   least 16 bytes really does indicate a truncated
+			   file. */
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated ZIP file data");
+			return (ARCHIVE_FATAL);
+		}
+		/* Check for a complete PK\007\010 signature. */
+		p = buff;
+		if (p[0] == 'P' && p[1] == 'K' 
+		    && p[2] == '\007' && p[3] == '\010'
+		    && archive_le32dec(p + 4) == zip->entry_crc32
+		    && archive_le32dec(p + 8) == zip->entry_compressed_bytes_read
+		    && archive_le32dec(p + 12) == zip->entry_uncompressed_bytes_read) {
+			zip->entry->crc32 = archive_le32dec(p + 4);
+			zip->entry->compressed_size = archive_le32dec(p + 8);
+			zip->entry->uncompressed_size = archive_le32dec(p + 12);
+			zip->end_of_entry = 1;
+			zip->unconsumed = 16;
+			return (ARCHIVE_OK);
+		}
+		/* If not at EOF, ensure we consume at least one byte. */
+		++p;
+
+		/* Scan forward until we see where a PK\007\010 signature might be. */
+		/* Return bytes up until that point.  On the next call, the code
+		   above will verify the data descriptor. */
+		while (p < buff + bytes_avail - 4) {
+			if (p[3] == 'P') { p += 3; }
+			else if (p[3] == 'K') { p += 2; }
+			else if (p[3] == '\007') { p += 1; }
+			else if (p[3] == '\010' && p[2] == '\007'
+			    && p[1] == 'K' && p[0] == 'P') {
+				break;
+			} else { p += 4; }
+		}
+		bytes_avail = p - buff;
+	} else {
+		if (zip->entry_bytes_remaining == 0) {
+			zip->end_of_entry = 1;
+			return (ARCHIVE_OK);
+		}
+		/* Grab a bunch of bytes. */
+		buff = __archive_read_ahead(a, 1, &bytes_avail);
+		if (bytes_avail <= 0) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated ZIP file data");
+			return (ARCHIVE_FATAL);
+		}
+		if (bytes_avail > zip->entry_bytes_remaining)
+			bytes_avail = zip->entry_bytes_remaining;
 	}
-	/*
-	 * Note: '1' here is a performance optimization.
-	 * Recall that the decompression layer returns a count of
-	 * available bytes; asking for more than that forces the
-	 * decompressor to combine reads by copying data.
-	 */
-	*buff = __archive_read_ahead(a, 1, &bytes_avail);
-	if (bytes_avail <= 0) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Truncated ZIP file data");
-		return (ARCHIVE_FATAL);
-	}
-	if (bytes_avail > zip->entry_bytes_remaining)
-		bytes_avail = zip->entry_bytes_remaining;
-	__archive_read_consume(a, bytes_avail);
 	*size = bytes_avail;
-	*offset = zip->entry_offset;
-	zip->entry_offset += *size;
-	zip->entry_bytes_remaining -= *size;
-	zip->entry_uncompressed_bytes_read += *size;
-	zip->entry_compressed_bytes_read += *size;
+	zip->entry_bytes_remaining -= bytes_avail;
+	zip->entry_uncompressed_bytes_read += bytes_avail;
+	zip->entry_compressed_bytes_read += bytes_avail;
+	zip->unconsumed += bytes_avail;
+	*_buff = buff;
 	return (ARCHIVE_OK);
 }
 
 #ifdef HAVE_ZLIB_H
 static int
 zip_read_data_deflate(struct archive_read *a, const void **buff,
-    size_t *size, off_t *offset)
+    size_t *size, int64_t *offset)
 {
 	struct zip *zip;
 	ssize_t bytes_avail;
 	const void *compressed_buff;
 	int r;
 
+	(void)offset; /* UNUSED */
+
 	zip = (struct zip *)(a->format->data);
 
 	/* If the buffer hasn't been allocated, allocate it now. */
 	if (zip->uncompressed_buffer == NULL) {
-		zip->uncompressed_buffer_size = 32 * 1024;
+		zip->uncompressed_buffer_size = 256 * 1024;
 		zip->uncompressed_buffer
 		    = (unsigned char *)malloc(zip->uncompressed_buffer_size);
 		if (zip->uncompressed_buffer == NULL) {
@@ -731,6 +988,10 @@
 	 * decompressor to combine reads by copying data.
 	 */
 	compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);
+	if (0 == (zip->entry->flags & ZIP_LENGTH_AT_END)
+	    && bytes_avail > zip->entry_bytes_remaining) {
+		bytes_avail = zip->entry_bytes_remaining;
+	}
 	if (bytes_avail <= 0) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 		    "Truncated ZIP file body");
@@ -773,66 +1034,102 @@
 	zip->entry_bytes_remaining -= bytes_avail;
 	zip->entry_compressed_bytes_read += bytes_avail;
 
-	*offset = zip->entry_offset;
 	*size = zip->stream.total_out;
-	zip->entry_uncompressed_bytes_read += *size;
+	zip->entry_uncompressed_bytes_read += zip->stream.total_out;
 	*buff = zip->uncompressed_buffer;
-	zip->entry_offset += *size;
+
+	if (zip->end_of_entry && (zip->entry->flags & ZIP_LENGTH_AT_END)) {
+		const char *p;
+
+		if (NULL == (p = __archive_read_ahead(a, 16, NULL))) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated ZIP end-of-file record");
+			return (ARCHIVE_FATAL);
+		}
+		/* Consume the optional PK\007\010 marker. */
+		if (p[0] == 'P' && p[1] == 'K' && p[2] == '\007' && p[3] == '\010') {
+			zip->entry->crc32 = archive_le32dec(p + 4);
+			zip->entry->compressed_size = archive_le32dec(p + 8);
+			zip->entry->uncompressed_size = archive_le32dec(p + 12);
+			zip->unconsumed = 16;
+		}
+	}
+
 	return (ARCHIVE_OK);
 }
-#else
-static int
-zip_read_data_deflate(struct archive_read *a, const void **buff,
-    size_t *size, off_t *offset)
-{
-	*buff = NULL;
-	*size = 0;
-	*offset = 0;
-	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-	    "libarchive compiled without deflate support (no libz)");
-	return (ARCHIVE_FATAL);
-}
 #endif
 
 static int
 archive_read_format_zip_read_data_skip(struct archive_read *a)
 {
 	struct zip *zip;
-	const void *buff = NULL;
-	off_t bytes_skipped;
 
 	zip = (struct zip *)(a->format->data);
 
 	/* If we've already read to end of data, we're done. */
 	if (zip->end_of_entry)
 		return (ARCHIVE_OK);
+	/* If we're seeking, we're done. */
+	if (zip->have_central_directory)
+		return (ARCHIVE_OK);
 
-	/*
-	 * If the length is at the end, we have no choice but
-	 * to decompress all the data to find the end marker.
-	 */
-	if (zip->flags & ZIP_LENGTH_AT_END) {
-		size_t size;
-		off_t offset;
-		int r;
-		do {
-			r = archive_read_format_zip_read_data(a, &buff,
-			    &size, &offset);
-		} while (r == ARCHIVE_OK);
-		return (r);
+	/* So we know we're streaming... */
+	if (0 == (zip->entry->flags & ZIP_LENGTH_AT_END)) {
+		/* We know the compressed length, so we can just skip. */
+		int64_t bytes_skipped = __archive_read_consume(a,
+		    zip->entry_bytes_remaining + zip->unconsumed);
+		if (bytes_skipped < 0)
+			return (ARCHIVE_FATAL);
+		zip->unconsumed = 0;
+		return (ARCHIVE_OK);
 	}
 
-	/*
-	 * If the length is at the beginning, we can skip the
-	 * compressed data much more quickly.
-	 */
-	bytes_skipped = __archive_read_skip(a, zip->entry_bytes_remaining);
-	if (bytes_skipped < 0)
-		return (ARCHIVE_FATAL);
-
-	/* This entry is finished and done. */
-	zip->end_of_entry = 1;
-	return (ARCHIVE_OK);
+	/* We're streaming and we don't know the length. */
+	/* If the body is compressed and we know the format, we can
+	 * find an exact end-of-entry by decompressing it. */
+	switch (zip->entry->compression) {
+#ifdef HAVE_ZLIB_H
+	case 8: /* Deflate compression. */
+		while (!zip->end_of_entry) {
+			int64_t offset = 0;
+			const void *buff = NULL;
+			size_t size = 0;
+			int r;
+			r =  zip_read_data_deflate(a, &buff, &size, &offset);
+			if (r != ARCHIVE_OK)
+				return (r);
+		}
+		break;
+#endif
+	default: /* Uncompressed or unknown. */
+		/* Scan for a PK\007\010 signature. */
+		__archive_read_consume(a, zip->unconsumed);
+		zip->unconsumed = 0;
+		for (;;) {
+			const char *p, *buff;
+			ssize_t bytes_avail;
+			buff = __archive_read_ahead(a, 16, &bytes_avail);
+			if (bytes_avail < 16) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Truncated ZIP file data");
+				return (ARCHIVE_FATAL);
+			}
+			p = buff;
+			while (p <= buff + bytes_avail - 16) {
+				if (p[3] == 'P') { p += 3; }
+				else if (p[3] == 'K') { p += 2; }
+				else if (p[3] == '\007') { p += 1; }
+				else if (p[3] == '\010' && p[2] == '\007'
+				    && p[1] == 'K' && p[0] == 'P') {
+					__archive_read_consume(a, p - buff + 16);
+					return ARCHIVE_OK;
+				} else { p += 4; }
+			}
+			__archive_read_consume(a, p - buff);
+		}
+	}
+	return ARCHIVE_OK;
 }
 
 static int
@@ -845,8 +1142,8 @@
 	if (zip->stream_valid)
 		inflateEnd(&zip->stream);
 #endif
+	free(zip->zip_entries);
 	free(zip->uncompressed_buffer);
-	archive_string_free(&(zip->pathname));
 	archive_string_free(&(zip->extra));
 	free(zip);
 	(a->format->data) = NULL;
@@ -859,28 +1156,30 @@
  *  triplets.  id and size are 2 bytes each.
  */
 static void
-process_extra(const void* extra, struct zip* zip)
+process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry)
 {
-	int offset = 0;
-	const char *p = (const char *)extra;
-	while (offset < zip->extra_length - 4)
+	unsigned offset = 0;
+
+	while (offset < extra_length - 4)
 	{
 		unsigned short headerid = archive_le16dec(p + offset);
 		unsigned short datasize = archive_le16dec(p + offset + 2);
 		offset += 4;
-		if (offset + datasize > zip->extra_length)
+		if (offset + datasize > extra_length)
 			break;
 #ifdef DEBUG
-		fprintf(stderr, "Header id 0x%04x, length %d\n",
+		fprintf(stderr, "Header id 0x%x, length %d\n",
 		    headerid, datasize);
 #endif
 		switch (headerid) {
 		case 0x0001:
 			/* Zip64 extended information extra field. */
 			if (datasize >= 8)
-				zip->uncompressed_size = archive_le64dec(p + offset);
+				zip_entry->uncompressed_size =
+				    archive_le64dec(p + offset);
 			if (datasize >= 16)
-				zip->compressed_size = archive_le64dec(p + offset + 8);
+				zip_entry->compressed_size =
+				    archive_le64dec(p + offset + 8);
 			break;
 		case 0x5455:
 		{
@@ -893,12 +1192,12 @@
 			{
 #ifdef DEBUG
 				fprintf(stderr, "mtime: %lld -> %d\n",
-				    (long long)zip->mtime,
+				    (long long)zip_entry->mtime,
 				    archive_le32dec(p + offset));
 #endif
 				if (datasize < 4)
 					break;
-				zip->mtime = archive_le32dec(p + offset);
+				zip_entry->mtime = archive_le32dec(p + offset);
 				offset += 4;
 				datasize -= 4;
 			}
@@ -906,7 +1205,7 @@
 			{
 				if (datasize < 4)
 					break;
-				zip->atime = archive_le32dec(p + offset);
+				zip_entry->atime = archive_le32dec(p + offset);
 				offset += 4;
 				datasize -= 4;
 			}
@@ -914,12 +1213,25 @@
 			{
 				if (datasize < 4)
 					break;
-				zip->ctime = archive_le32dec(p + offset);
+				zip_entry->ctime = archive_le32dec(p + offset);
 				offset += 4;
 				datasize -= 4;
 			}
 			break;
 		}
+		case 0x5855:
+		{
+			/* Info-ZIP Unix Extra Field (old version) "UX". */
+			if (datasize >= 8) {
+				zip_entry->atime = archive_le32dec(p + offset);
+				zip_entry->mtime = archive_le32dec(p + offset + 4);
+			}
+			if (datasize >= 12) {
+				zip_entry->uid = archive_le16dec(p + offset + 8);
+				zip_entry->gid = archive_le16dec(p + offset + 10);
+			}
+			break;
+		}
 		case 0x7855:
 			/* Info-ZIP Unix Extra Field (type 2) "Ux". */
 #ifdef DEBUG
@@ -928,23 +1240,50 @@
 			    archive_le16dec(p + offset + 2));
 #endif
 			if (datasize >= 2)
-				zip->uid = archive_le16dec(p + offset);
+				zip_entry->uid = archive_le16dec(p + offset);
 			if (datasize >= 4)
-				zip->gid = archive_le16dec(p + offset + 2);
+				zip_entry->gid = archive_le16dec(p + offset + 2);
 			break;
 		case 0x7875:
+		{
 			/* Info-Zip Unix Extra Field (type 3) "ux". */
+			int uidsize = 0, gidsize = 0;
+
+			if (datasize >= 1 && p[offset] == 1) {/* version=1 */
+				if (datasize >= 4) {
+					/* get a uid size. */
+					uidsize = p[offset+1];
+					if (uidsize == 2)
+						zip_entry->uid = archive_le16dec(
+						     p + offset + 2);
+					else if (uidsize == 4 && datasize >= 6)
+						zip_entry->uid = archive_le32dec(
+						     p + offset + 2);
+				}
+				if (datasize >= (2 + uidsize + 3)) {
+					/* get a gid size. */
+					gidsize = p[offset+2+uidsize];
+					if (gidsize == 2)
+						zip_entry->gid = archive_le16dec(
+						    p+offset+2+uidsize+1);
+					else if (gidsize == 4 &&
+					    datasize >= (2 + uidsize + 5))
+						zip_entry->gid = archive_le32dec(
+						    p+offset+2+uidsize+1);
+				}
+			}
 			break;
+		}
 		default:
 			break;
 		}
 		offset += datasize;
 	}
 #ifdef DEBUG
-	if (offset != zip->extra_length)
+	if (offset != extra_length)
 	{
 		fprintf(stderr,
-		    "Extra data field contents do not match reported size!");
+		    "Extra data field contents do not match reported size!\n");
 	}
 #endif
 }
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_string.c
--- a/head/contrib/libarchive/libarchive/archive_string.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_string.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,6 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2003-2011 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,13 +25,29 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_string.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_string.c 232153 2012-02-25 10:58:02Z mm $");
 
 /*
  * Basic resizable string support, to simplify manipulating arbitrary-sized
  * strings while minimizing heap activity.
+ *
+ * In particular, the buffer used by a string object is only grown, it
+ * never shrinks, so you can clear and reuse the same string object
+ * without incurring additional memory allocations.
  */
 
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_ICONV_H
+#include <iconv.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#ifdef HAVE_LOCALCHARSET_H
+#include <localcharset.h>
+#endif
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
@@ -42,63 +59,224 @@
 #endif
 #if defined(_WIN32) && !defined(__CYGWIN__)
 #include <windows.h>
+#include <locale.h>
 #endif
-
+#if defined(__APPLE__)
+#include <CoreServices/CoreServices.h>
+#endif
+
+#include "archive_endian.h"
 #include "archive_private.h"
 #include "archive_string.h"
-
-struct archive_string *
-__archive_string_append(struct archive_string *as, const char *p, size_t s)
+#include "archive_string_composition.h"
+
+#if !defined(HAVE_WMEMCPY) && !defined(wmemcpy)
+#define wmemcpy(a,b,i)  (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+struct archive_string_conv {
+	struct archive_string_conv	*next;
+	char				*from_charset;
+	char				*to_charset;
+	unsigned			 from_cp;
+	unsigned			 to_cp;
+	/* Set 1 if from_charset and to_charset are the same. */
+	int				 same;
+	int				 flag;
+#define SCONV_TO_CHARSET	1	/* MBS is being converted to specified
+					 * charset. */
+#define SCONV_FROM_CHARSET	(1<<1)	/* MBS is being converted from
+					 * specified charset. */
+#define SCONV_BEST_EFFORT 	(1<<2)	/* Copy at least ASCII code. */
+#define SCONV_WIN_CP	 	(1<<3)	/* Use Windows API for converting
+					 * MBS. */
+#define SCONV_UTF8_LIBARCHIVE_2 (1<<4)	/* Incorrect UTF-8 made by libarchive
+					 * 2.x in the wrong assumption. */
+#define SCONV_NORMALIZATION_C	(1<<6)	/* Need normalization to be Form C.
+					 * Before UTF-8 characters are actually
+					 * processed. */
+#define SCONV_NORMALIZATION_D	(1<<7)	/* Need normalization to be Form D.
+					 * Before UTF-8 characters are actually
+					 * processed.
+					 * Currently this only for MAC OS X. */
+#define SCONV_TO_UTF8		(1<<8)	/* "to charset" side is UTF-8. */
+#define SCONV_FROM_UTF8		(1<<9)	/* "from charset" side is UTF-8. */
+#define SCONV_TO_UTF16BE 	(1<<10)	/* "to charset" side is UTF-16BE. */
+#define SCONV_FROM_UTF16BE 	(1<<11)	/* "from charset" side is UTF-16BE. */
+#define SCONV_TO_UTF16LE 	(1<<12)	/* "to charset" side is UTF-16LE. */
+#define SCONV_FROM_UTF16LE 	(1<<13)	/* "from charset" side is UTF-16LE. */
+#define SCONV_TO_UTF16		(SCONV_TO_UTF16BE | SCONV_TO_UTF16LE)
+#define SCONV_FROM_UTF16	(SCONV_FROM_UTF16BE | SCONV_FROM_UTF16LE)
+
+#if HAVE_ICONV
+	iconv_t				 cd;
+	iconv_t				 cd_w;/* Use at archive_mstring on
+				 	       * Windows. */
+#endif
+	/* A temporary buffer for normalization. */
+	struct archive_string		 utftmp;
+#if defined(__APPLE__)
+	UnicodeToTextInfo		 uniInfo;
+	struct archive_string		 utf16nfc;
+	struct archive_string		 utf16nfd;
+#endif
+	int (*converter[2])(struct archive_string *, const void *, size_t,
+	    struct archive_string_conv *);
+	int				 nconverter;
+};
+
+#define CP_C_LOCALE	0	/* "C" locale only for this file. */
+#define CP_UTF16LE	1200
+#define CP_UTF16BE	1201
+
+#define IS_HIGH_SURROGATE_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDBFF)
+#define IS_LOW_SURROGATE_LA(uc)	 ((uc) >= 0xDC00 && (uc) <= 0xDFFF)
+#define IS_SURROGATE_PAIR_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDFFF)
+#define UNICODE_MAX		0x10FFFF
+#define UNICODE_R_CHAR		0xFFFD	/* Replacement character. */
+/* Set U+FFFD(Replacement character) in UTF-8. */
+#define UTF8_SET_R_CHAR(outp) do {		\
+			(outp)[0] = 0xef;	\
+			(outp)[1] = 0xbf;	\
+			(outp)[2] = 0xbd;	\
+} while (0)
+#define UTF8_R_CHAR_SIZE	3
+
+static struct archive_string_conv *find_sconv_object(struct archive *,
+	const char *, const char *);
+static void add_sconv_object(struct archive *, struct archive_string_conv *);
+static struct archive_string_conv *create_sconv_object(const char *,
+	const char *, unsigned, int);
+static void free_sconv_object(struct archive_string_conv *);
+static struct archive_string_conv *get_sconv_object(struct archive *,
+	const char *, const char *, int);
+static unsigned make_codepage_from_charset(const char *);
+static unsigned get_current_codepage(void);
+static unsigned get_current_oemcp(void);
+static size_t mbsnbytes(const void *, size_t);
+static size_t utf16nbytes(const void *, size_t);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+static int archive_wstring_append_from_mbs_in_codepage(
+    struct archive_wstring *, const char *, size_t,
+    struct archive_string_conv *);
+static int archive_string_append_from_wcs_in_codepage(struct archive_string *,
+    const wchar_t *, size_t, struct archive_string_conv *);
+static int is_big_endian(void);
+static int strncat_in_codepage(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int win_strncat_from_utf16be(struct archive_string *, const void *, size_t,
+    struct archive_string_conv *);
+static int win_strncat_from_utf16le(struct archive_string *, const void *, size_t,
+    struct archive_string_conv *);
+static int win_strncat_to_utf16be(struct archive_string *, const void *, size_t,
+    struct archive_string_conv *);
+static int win_strncat_to_utf16le(struct archive_string *, const void *, size_t,
+    struct archive_string_conv *);
+#endif
+static int best_effort_strncat_from_utf16be(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int best_effort_strncat_from_utf16le(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int best_effort_strncat_to_utf16be(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int best_effort_strncat_to_utf16le(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+#if defined(HAVE_ICONV)
+static int iconv_strncat_in_locale(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+#endif
+static int best_effort_strncat_in_locale(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int _utf8_to_unicode(uint32_t *, const char *, size_t);
+static int utf8_to_unicode(uint32_t *, const char *, size_t);
+static inline uint32_t combine_surrogate_pair(uint32_t, uint32_t);
+static int cesu8_to_unicode(uint32_t *, const char *, size_t);
+static size_t unicode_to_utf8(char *, size_t, uint32_t);
+static int utf16_to_unicode(uint32_t *, const char *, size_t, int);
+static size_t unicode_to_utf16be(char *, size_t, uint32_t);
+static size_t unicode_to_utf16le(char *, size_t, uint32_t);
+static int strncat_from_utf8_libarchive2(struct archive_string *,
+    const void *, size_t, struct archive_string_conv *);
+static int strncat_from_utf8_to_utf8(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int archive_string_normalize_C(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+#if defined(__APPLE__)
+static int archive_string_normalize_D(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+#endif
+static int archive_string_append_unicode(struct archive_string *,
+    const void *, size_t, struct archive_string_conv *);
+
+static struct archive_string *
+archive_string_append(struct archive_string *as, const char *p, size_t s)
 {
-	if (__archive_string_ensure(as, as->length + s + 1) == NULL)
-		__archive_errx(1, "Out of memory");
+	if (archive_string_ensure(as, as->length + s + 1) == NULL)
+		return (NULL);
 	memcpy(as->s + as->length, p, s);
-	as->s[as->length + s] = 0;
 	as->length += s;
+	as->s[as->length] = 0;
 	return (as);
 }
 
+static struct archive_wstring *
+archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s)
+{
+	if (archive_wstring_ensure(as, as->length + s + 1) == NULL)
+		return (NULL);
+	wmemcpy(as->s + as->length, p, s);
+	as->length += s;
+	as->s[as->length] = 0;
+	return (as);
+}
+
 void
-__archive_string_copy(struct archive_string *dest, struct archive_string *src)
+archive_string_concat(struct archive_string *dest, struct archive_string *src)
 {
-	if (src->length == 0)
-		dest->length = 0;
-	else {
-		if (__archive_string_ensure(dest, src->length + 1) == NULL)
-			__archive_errx(1, "Out of memory");
-		memcpy(dest->s, src->s, src->length);
-		dest->length = src->length;
-		dest->s[dest->length] = 0;
-	}
+	if (archive_string_append(dest, src->s, src->length) == NULL)
+		__archive_errx(1, "Out of memory");
 }
 
 void
-__archive_string_concat(struct archive_string *dest, struct archive_string *src)
+archive_wstring_concat(struct archive_wstring *dest, struct archive_wstring *src)
 {
-	if (src->length > 0) {
-		if (__archive_string_ensure(dest, dest->length + src->length + 1) == NULL)
-			__archive_errx(1, "Out of memory");
-		memcpy(dest->s + dest->length, src->s, src->length);
-		dest->length += src->length;
-		dest->s[dest->length] = 0;
-	}
+	if (archive_wstring_append(dest, src->s, src->length) == NULL)
+		__archive_errx(1, "Out of memory");
 }
 
 void
-__archive_string_free(struct archive_string *as)
+archive_string_free(struct archive_string *as)
 {
 	as->length = 0;
 	as->buffer_length = 0;
-	if (as->s != NULL) {
-		free(as->s);
-		as->s = NULL;
-	}
+	free(as->s);
+	as->s = NULL;
+}
+
+void
+archive_wstring_free(struct archive_wstring *as)
+{
+	as->length = 0;
+	as->buffer_length = 0;
+	free(as->s);
+	as->s = NULL;
+}
+
+struct archive_wstring *
+archive_wstring_ensure(struct archive_wstring *as, size_t s)
+{
+	return (struct archive_wstring *)
+		archive_string_ensure((struct archive_string *)as,
+					s * sizeof(wchar_t));
 }
 
 /* Returns NULL on any allocation failure. */
 struct archive_string *
-__archive_string_ensure(struct archive_string *as, size_t s)
+archive_string_ensure(struct archive_string *as, size_t s)
 {
+	char *p;
+	size_t new_length;
+
 	/* If buffer is already big enough, don't reallocate. */
 	if (as->s && (s <= as->buffer_length))
 		return (as);
@@ -112,18 +290,18 @@
 	 */
 	if (as->buffer_length < 32)
 		/* Start with a minimum 32-character buffer. */
-		as->buffer_length = 32;
+		new_length = 32;
 	else if (as->buffer_length < 8192)
 		/* Buffers under 8k are doubled for speed. */
-		as->buffer_length += as->buffer_length;
+		new_length = as->buffer_length + as->buffer_length;
 	else {
 		/* Buffers 8k and over grow by at least 25% each time. */
-		size_t old_length = as->buffer_length;
-		as->buffer_length += as->buffer_length / 4;
-		/* Be safe: If size wraps, release buffer and return NULL. */
-		if (as->buffer_length < old_length) {
-			free(as->s);
-			as->s = NULL;
+		new_length = as->buffer_length + as->buffer_length / 4;
+		/* Be safe: If size wraps, fail. */
+		if (new_length < as->buffer_length) {
+			/* On failure, wipe the string and return NULL. */
+			archive_string_free(as);
+			errno = ENOMEM;/* Make sure errno has ENOMEM. */
 			return (NULL);
 		}
 	}
@@ -132,17 +310,31 @@
 	 * grow the buffer.  In any case, we have to grow it enough to
 	 * hold the request.
 	 */
-	if (as->buffer_length < s)
-		as->buffer_length = s;
+	if (new_length < s)
+		new_length = s;
 	/* Now we can reallocate the buffer. */
-	as->s = (char *)realloc(as->s, as->buffer_length);
-	if (as->s == NULL)
+	p = (char *)realloc(as->s, new_length);
+	if (p == NULL) {
+		/* On failure, wipe the string and return NULL. */
+		archive_string_free(as);
+		errno = ENOMEM;/* Make sure errno has ENOMEM. */
 		return (NULL);
+	}
+
+	as->s = p;
+	as->buffer_length = new_length;
 	return (as);
 }
 
+/*
+ * TODO: See if there's a way to avoid scanning
+ * the source string twice.  Then test to see
+ * if it actually helps (remember that we're almost
+ * always called with pretty short arguments, so
+ * such an optimization might not help).
+ */
 struct archive_string *
-__archive_strncat(struct archive_string *as, const void *_p, size_t n)
+archive_strncat(struct archive_string *as, const void *_p, size_t n)
 {
 	size_t s;
 	const char *p, *pp;
@@ -156,263 +348,537 @@
 		pp++;
 		s++;
 	}
-	return (__archive_string_append(as, p, s));
+	if ((as = archive_string_append(as, p, s)) == NULL)
+		__archive_errx(1, "Out of memory");
+	return (as);
 }
 
+struct archive_wstring *
+archive_wstrncat(struct archive_wstring *as, const wchar_t *p, size_t n)
+{
+	size_t s;
+	const wchar_t *pp;
+
+	/* Like strlen(p), except won't examine positions beyond p[n]. */
+	s = 0;
+	pp = p;
+	while (s < n && *pp) {
+		pp++;
+		s++;
+	}
+	if ((as = archive_wstring_append(as, p, s)) == NULL)
+		__archive_errx(1, "Out of memory");
+	return (as);
+}
+
 struct archive_string *
-__archive_strappend_char(struct archive_string *as, char c)
+archive_strcat(struct archive_string *as, const void *p)
 {
-	return (__archive_string_append(as, &c, 1));
+	/* strcat is just strncat without an effective limit. 
+	 * Assert that we'll never get called with a source
+	 * string over 16MB.
+	 * TODO: Review all uses of strcat in the source
+	 * and try to replace them with strncat().
+	 */
+	return archive_strncat(as, p, 0x1000000);
 }
 
+struct archive_wstring *
+archive_wstrcat(struct archive_wstring *as, const wchar_t *p)
+{
+	/* Ditto. */
+	return archive_wstrncat(as, p, 0x1000000);
+}
+
+struct archive_string *
+archive_strappend_char(struct archive_string *as, char c)
+{
+	if ((as = archive_string_append(as, &c, 1)) == NULL)
+		__archive_errx(1, "Out of memory");
+	return (as);
+}
+
+struct archive_wstring *
+archive_wstrappend_wchar(struct archive_wstring *as, wchar_t c)
+{
+	if ((as = archive_wstring_append(as, &c, 1)) == NULL)
+		__archive_errx(1, "Out of memory");
+	return (as);
+}
+
 /*
- * Translates a wide character string into UTF-8 and appends
- * to the archive_string.  Note: returns NULL if conversion fails,
- * but still leaves a best-effort conversion in the argument as.
+ * Get the "current character set" name to use with iconv.
+ * On FreeBSD, the empty character set name "" chooses
+ * the correct character encoding for the current locale,
+ * so this isn't necessary.
+ * But iconv on Mac OS 10.6 doesn't seem to handle this correctly;
+ * on that system, we have to explicitly call nl_langinfo()
+ * to get the right name.  Not sure about other platforms.
+ *
+ * NOTE: GNU libiconv does not recognize the character-set name
+ * which some platform nl_langinfo(CODESET) returns, so we should
+ * use locale_charset() instead of nl_langinfo(CODESET) for GNU libiconv.
  */
-struct archive_string *
-__archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w)
+static const char *
+default_iconv_charset(const char *charset) {
+	if (charset != NULL && charset[0] != '\0')
+		return charset;
+#if HAVE_LOCALE_CHARSET && !defined(__APPLE__)
+	/* locale_charset() is broken on Mac OS */
+	return locale_charset();
+#elif HAVE_NL_LANGINFO
+	return nl_langinfo(CODESET);
+#else
+	return "";
+#endif
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Convert MBS to WCS.
+ * Note: returns -1 if conversion fails.
+ */
+int
+archive_wstring_append_from_mbs(struct archive_wstring *dest,
+    const char *p, size_t len)
 {
-	char *p;
-	unsigned wc;
-	char buff[256];
-	struct archive_string *return_val = as;
-
+	int r = archive_wstring_append_from_mbs_in_codepage(dest, p, len, NULL);
+	if (r != 0 && errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (r);
+}
+
+static int
+archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest,
+    const char *s, size_t length, struct archive_string_conv *sc)
+{
+	int count, ret = 0;
+	UINT from_cp;
+
+	if (sc != NULL)
+		from_cp = sc->from_cp;
+	else
+		from_cp = get_current_codepage();
+
+	if (from_cp == CP_C_LOCALE) {
+		/*
+		 * "C" locale special process.
+		 */
+		wchar_t *ws;
+		const unsigned char *mp;
+
+		if (NULL == archive_wstring_ensure(dest,
+		    dest->length + length + 1))
+			return (-1);
+
+		ws = dest->s + dest->length;
+		mp = (const unsigned char *)s;
+		count = 0;
+		while (count < (int)length && *mp) {
+			*ws++ = (wchar_t)*mp++;
+			count++;
+		}
+	} else if (sc != NULL && (sc->flag & SCONV_NORMALIZATION_C)) {
+		/*
+		 * Normalize UTF-8 and UTF-16BE and convert it directly
+		 * to UTF-16 as wchar_t.
+		 */
+		struct archive_string u16;
+		int saved_flag = sc->flag;/* save current flag. */
+
+		if (is_big_endian())
+			sc->flag |= SCONV_TO_UTF16BE;
+		else
+			sc->flag |= SCONV_TO_UTF16LE;
+
+		if (sc->flag & SCONV_FROM_UTF16) {
+			/*
+			 *  UTF-16BE/LE NFD ===> UTF-16 NFC
+			 */
+			count = utf16nbytes(s, length);
+		} else {
+			/*
+			 *  UTF-8 NFD ===> UTF-16 NFC
+			 */
+			count = mbsnbytes(s, length);
+		}
+		u16.s = (char *)dest->s;
+		u16.length = dest->length << 1;;
+		u16.buffer_length = dest->buffer_length;
+		ret = archive_string_normalize_C(&u16, s, count, sc);
+		dest->s = (wchar_t *)u16.s;
+		dest->length = u16.length >> 1;
+		dest->buffer_length = u16.buffer_length;
+		sc->flag = saved_flag;/* restore the saved flag. */
+		return (ret);
+	} else if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) {
+		count = utf16nbytes(s, length);
+		count >>= 1; /* to be WCS length */
+		/* Allocate memory for WCS. */
+		if (NULL == archive_wstring_ensure(dest,
+		    dest->length + count + 1))
+			return (-1);
+		wmemcpy(dest->s + dest->length, (wchar_t *)s, count);
+		if ((sc->flag & SCONV_FROM_UTF16BE) && !is_big_endian()) {
+			uint16_t *u16 = (uint16_t *)(dest->s + dest->length);
+			int b;
+			for (b = 0; b < count; b++) {
+				uint16_t val = archive_le16dec(u16+b);
+				archive_be16enc(u16+b, val);
+			}
+		} else if ((sc->flag & SCONV_FROM_UTF16LE) && is_big_endian()) {
+			uint16_t *u16 = (uint16_t *)(dest->s + dest->length);
+			int b;
+			for (b = 0; b < count; b++) {
+				uint16_t val = archive_be16dec(u16+b);
+				archive_le16enc(u16+b, val);
+			}
+		}
+	} else {
+		DWORD mbflag;
+
+		if (sc == NULL)
+			mbflag = 0;
+		else if (sc->flag & SCONV_FROM_CHARSET) {
+			/* Do not trust the length which comes from
+			 * an archive file. */
+			length = mbsnbytes(s, length);
+			mbflag = 0;
+		} else
+			mbflag = MB_PRECOMPOSED;
+
+		if (length == 0) {
+			/*
+			 * We do not need to convert any characters but make
+			 * sure `dest' has a valid buffer(no NULL pointer).
+			 */
+			if (NULL == archive_wstring_ensure(dest,
+			    dest->length + 1))
+				return (-1);
+			dest->s[dest->length] = L'\0';
+			return (0);
+		}
+
+		/*
+		 * Count how many bytes are needed for WCS.
+		 */
+		count = MultiByteToWideChar(from_cp,
+		    mbflag, s, length, NULL, 0);
+		if (count == 0) {
+			if (dest->s == NULL) {
+				if (NULL == archive_wstring_ensure(dest,
+				    dest->length + 1))
+					return (-1);
+			}
+			dest->s[dest->length] = L'\0';
+			return (-1);
+		}
+		/* Allocate memory for WCS. */
+		if (NULL == archive_wstring_ensure(dest,
+		    dest->length + count + 1))
+			return (-1);
+		/* Convert MBS to WCS. */
+		count = MultiByteToWideChar(from_cp,
+		    mbflag, s, length, dest->s + dest->length, count);
+		if (count == 0)
+			ret = -1;
+	}
+	dest->length += count;
+	dest->s[dest->length] = L'\0';
+	return (ret);
+}
+
+#elif defined(HAVE_MBSNRTOWCS)
+
+/*
+ * Convert MBS to WCS.
+ * Note: returns -1 if conversion fails.
+ */
+int
+archive_wstring_append_from_mbs(struct archive_wstring *dest,
+    const char *p, size_t len)
+{
+	size_t r;
 	/*
-	 * Convert one wide char at a time into 'buff', whenever that
-	 * fills, append it to the string.
+	 * No single byte will be more than one wide character,
+	 * so this length estimate will always be big enough.
 	 */
-	p = buff;
-	while (*w != L'\0') {
-		/* Flush the buffer when we have <=16 bytes free. */
-		/* (No encoding has a single character >16 bytes.) */
-		if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - 16)) {
-			*p = '\0';
-			archive_strcat(as, buff);
-			p = buff;
-		}
-		wc = *w++;
-		/* If this is a surrogate pair, assemble the full code point.*/
-		/* Note: wc must not be wchar_t here, because the full code
-		 * point can be more than 16 bits! */
-		if (wc >= 0xD800 && wc <= 0xDBff
-		    && *w >= 0xDC00 && *w <= 0xDFFF) {
-			wc -= 0xD800;
-			wc *= 0x400;
-			wc += (*w - 0xDC00);
-			wc += 0x10000;
-			++w;
-		}
-		/* Translate code point to UTF8 */
-		if (wc <= 0x7f) {
-			*p++ = (char)wc;
-		} else if (wc <= 0x7ff) {
-			*p++ = 0xc0 | ((wc >> 6) & 0x1f);
-			*p++ = 0x80 | (wc & 0x3f);
-		} else if (wc <= 0xffff) {
-			*p++ = 0xe0 | ((wc >> 12) & 0x0f);
-			*p++ = 0x80 | ((wc >> 6) & 0x3f);
-			*p++ = 0x80 | (wc & 0x3f);
-		} else if (wc <= 0x1fffff) {
-			*p++ = 0xf0 | ((wc >> 18) & 0x07);
-			*p++ = 0x80 | ((wc >> 12) & 0x3f);
-			*p++ = 0x80 | ((wc >> 6) & 0x3f);
-			*p++ = 0x80 | (wc & 0x3f);
-		} else {
-			/* Unicode has no codes larger than 0x1fffff. */
-			/* TODO: use \uXXXX escape here instead of ? */
-			*p++ = '?';
-			return_val = NULL;
-		}
+	size_t wcs_length = len;
+	size_t mbs_length = len;
+	const char *mbs = p;
+	wchar_t *wcs;
+	mbstate_t shift_state;
+
+	memset(&shift_state, 0, sizeof(shift_state));
+	if (NULL == archive_wstring_ensure(dest, dest->length + wcs_length + 1))
+		__archive_errx(1,
+		    "No memory for archive_wstring_append_from_mbs()");
+	wcs = dest->s + dest->length;
+	r = mbsnrtowcs(wcs, &mbs, mbs_length, wcs_length, &shift_state);
+	if (r != (size_t)-1) {
+		dest->length += r;
+		dest->s[dest->length] = L'\0';
+		return (0);
 	}
-	*p = '\0';
-	archive_strcat(as, buff);
-	return (return_val);
-}
-
-static int
-utf8_to_unicode(int *pwc, const char *s, size_t n)
-{
-        int ch;
-
-        /*
-	 * Decode 1-4 bytes depending on the value of the first byte.
-	 */
-        ch = (unsigned char)*s;
-	if (ch == 0) {
-		return (0); /* Standard:  return 0 for end-of-string. */
-	}
-	if ((ch & 0x80) == 0) {
-                *pwc = ch & 0x7f;
-		return (1);
-        }
-	if ((ch & 0xe0) == 0xc0) {
-		if (n < 2)
-			return (-1);
-		if ((s[1] & 0xc0) != 0x80) return (-1);
-                *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
-		return (2);
-        }
-	if ((ch & 0xf0) == 0xe0) {
-		if (n < 3)
-			return (-1);
-		if ((s[1] & 0xc0) != 0x80) return (-1);
-		if ((s[2] & 0xc0) != 0x80) return (-1);
-                *pwc = ((ch & 0x0f) << 12)
-		    | ((s[1] & 0x3f) << 6)
-		    | (s[2] & 0x3f);
-		return (3);
-        }
-	if ((ch & 0xf8) == 0xf0) {
-		if (n < 4)
-			return (-1);
-		if ((s[1] & 0xc0) != 0x80) return (-1);
-		if ((s[2] & 0xc0) != 0x80) return (-1);
-		if ((s[3] & 0xc0) != 0x80) return (-1);
-                *pwc = ((ch & 0x07) << 18)
-		    | ((s[1] & 0x3f) << 12)
-		    | ((s[2] & 0x3f) << 6)
-		    | (s[3] & 0x3f);
-		return (4);
-        }
-	/* Invalid first byte. */
+	dest->s[dest->length] = L'\0';
 	return (-1);
 }
 
+#else
+
 /*
- * Return a wide-character Unicode string by converting this archive_string
- * from UTF-8.  We assume that systems with 16-bit wchar_t always use
- * UTF16 and systems with 32-bit wchar_t can accept UCS4.
+ * Convert MBS to WCS.
+ * Note: returns -1 if conversion fails.
  */
-wchar_t *
-__archive_string_utf8_w(struct archive_string *as)
+int
+archive_wstring_append_from_mbs(struct archive_wstring *dest,
+    const char *p, size_t len)
 {
-	wchar_t *ws, *dest;
-	int wc, wc2;/* Must be large enough for a 21-bit Unicode code point. */
-	const char *src;
-	int n;
-
-	ws = (wchar_t *)malloc((as->length + 1) * sizeof(wchar_t));
-	if (ws == NULL)
-		__archive_errx(1, "Out of memory");
-	dest = ws;
-	src = as->s;
-	while (*src != '\0') {
-		n = utf8_to_unicode(&wc, src, 8);
-		if (n == 0)
+	size_t r;
+	/*
+	 * No single byte will be more than one wide character,
+	 * so this length estimate will always be big enough.
+	 */
+	size_t wcs_length = len;
+	size_t mbs_length = len;
+	const char *mbs = p;
+	wchar_t *wcs;
+#if HAVE_MBRTOWC
+	mbstate_t shift_state;
+
+	memset(&shift_state, 0, sizeof(shift_state));
+#endif
+	if (NULL == archive_wstring_ensure(dest, dest->length + wcs_length + 1))
+		__archive_errx(1,
+		    "No memory for archive_wstring_append_from_mbs()");
+	wcs = dest->s + dest->length;
+	/*
+	 * We cannot use mbsrtowcs/mbstowcs here because those may convert
+	 * extra MBS when strlen(p) > len and one wide character consis of
+	 * multi bytes.
+	 */
+	while (wcs_length > 0 && *mbs && mbs_length > 0) {
+#if HAVE_MBRTOWC
+		r = mbrtowc(wcs, mbs, wcs_length, &shift_state);
+#else
+		r = mbtowc(wcs, mbs, wcs_length);
+#endif
+		if (r == (size_t)-1 || r == (size_t)-2) {
+			dest->s[dest->length] = L'\0';
+			return (-1);
+		}
+		if (r == 0 || r > mbs_length)
 			break;
-		if (n < 0) {
-			free(ws);
-			return (NULL);
-		}
-		src += n;
-		if (wc >= 0xDC00 && wc <= 0xDBFF) {
-			/* This is a leading surrogate; some idiot
-			 * has translated UTF16 to UTF8 without combining
-			 * surrogates; rebuild the full code point before
-			 * continuing. */
-			n = utf8_to_unicode(&wc2, src, 8);
-			if (n < 0) {
-				free(ws);
-				return (NULL);
-			}
-			if (n == 0) /* Ignore the leading surrogate */
-				break;
-			if (wc2 < 0xDC00 || wc2 > 0xDFFF) {
-				/* If the second character isn't a
-				 * trailing surrogate, then someone
-				 * has really screwed up and this is
-				 * invalid. */
-				free(ws);
-				return (NULL);
-			} else {
-				src += n;
-				wc -= 0xD800;
-				wc *= 0x400;
-				wc += wc2 - 0xDC00;
-				wc += 0x10000;
-			}
-		}
-		if ((sizeof(wchar_t) < 4) && (wc > 0xffff)) {
-			/* We have a code point that won't fit into a
-			 * wchar_t; convert it to a surrogate pair. */
-			wc -= 0x10000;
-			*dest++ = ((wc >> 10) & 0x3ff) + 0xD800;
-			*dest++ = (wc & 0x3ff) + 0xDC00;
-		} else
-			*dest++ = wc;
+		wcs++;
+		wcs_length--;
+		mbs += r;
+		mbs_length -= r;
 	}
-	*dest = L'\0';
-	return (ws);
+	dest->length = wcs - dest->s;
+	dest->s[dest->length] = L'\0';
+	return (0);
 }
 
+#endif
+
 #if defined(_WIN32) && !defined(__CYGWIN__)
 
 /*
- * Translates a wide character string into current locale character set
- * and appends to the archive_string.  Note: returns NULL if conversion
- * fails.
+ * WCS ==> MBS.
+ * Note: returns -1 if conversion fails.
  *
  * Win32 builds use WideCharToMultiByte from the Windows API.
  * (Maybe Cygwin should too?  WideCharToMultiByte will know a
  * lot more about local character encodings than the wcrtomb()
  * wrapper is going to know.)
  */
-struct archive_string *
-__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w)
+int
+archive_string_append_from_wcs(struct archive_string *as,
+    const wchar_t *w, size_t len)
 {
-	char *p;
-	int l, wl;
-	BOOL useDefaultChar = FALSE;
-
-	wl = (int)wcslen(w);
-	l = wl * 4 + 4;
-	p = malloc(l);
-	if (p == NULL)
-		__archive_errx(1, "Out of memory");
-	/* To check a useDefaultChar is to simulate error handling of
-	 * the my_wcstombs() which is running on non Windows system with
-	 * wctomb().
-	 * And to set NULL for last argument is necessary when a codepage
-	 * is not CP_ACP(current locale).
-	 */
-	l = WideCharToMultiByte(CP_ACP, 0, w, wl, p, l, NULL, &useDefaultChar);
-	if (l == 0) {
-		free(p);
-		return (NULL);
+	int r = archive_string_append_from_wcs_in_codepage(as, w, len, NULL);
+	if (r != 0 && errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (r);
+}
+
+static int
+archive_string_append_from_wcs_in_codepage(struct archive_string *as,
+    const wchar_t *ws, size_t len, struct archive_string_conv *sc)
+{
+	BOOL defchar_used, *dp;
+	int count, ret = 0;
+	UINT to_cp;
+	int wslen = (int)len;
+
+	if (sc != NULL)
+		to_cp = sc->to_cp;
+	else
+		to_cp = get_current_codepage();
+
+	if (to_cp == CP_C_LOCALE) {
+		/*
+		 * "C" locale special process.
+		 */
+		const wchar_t *wp = ws;
+		char *p;
+
+		if (NULL == archive_string_ensure(as,
+		    as->length + wslen +1))
+			return (-1);
+		p = as->s + as->length;
+		count = 0;
+		defchar_used = 0;
+		while (count < wslen && *wp) {
+			if (*wp > 255) {
+				*p++ = '?';
+				wp++;
+				defchar_used = 1;
+			} else
+				*p++ = (char)*wp++;
+			count++;
+		}
+	} else if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) {
+		uint16_t *u16;
+
+		if (NULL ==
+		    archive_string_ensure(as, as->length + len * 2 + 2))
+			return (-1);
+		u16 = (uint16_t *)(as->s + as->length);
+		count = 0;
+		defchar_used = 0;
+		if (sc->flag & SCONV_TO_UTF16BE) {
+			while (count < (int)len && *ws) {
+				archive_be16enc(u16+count, *ws);
+				ws++;
+				count++;
+			}
+		} else {
+			while (count < (int)len && *ws) {
+				archive_le16enc(u16+count, *ws);
+				ws++;
+				count++;
+			}
+		}
+		count <<= 1; /* to be byte size */
+	} else {
+		/* Make sure the MBS buffer has plenty to set. */
+		if (NULL ==
+		    archive_string_ensure(as, as->length + len * 2 + 1))
+			return (-1);
+		do {
+			defchar_used = 0;
+			if (to_cp == CP_UTF8 || sc == NULL)
+				dp = NULL;
+			else
+				dp = &defchar_used;
+			count = WideCharToMultiByte(to_cp, 0, ws, wslen,
+			    as->s + as->length, as->buffer_length-1, NULL, dp);
+			if (count == 0 &&
+			    GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+				/* Expand the MBS buffer and retry. */
+				if (NULL == archive_string_ensure(as,
+					as->buffer_length + len))
+					return (-1);
+				continue;
+			}
+			if (count == 0)
+				ret = -1;
+		} while (0);
 	}
-	__archive_string_append(as, p, l);
-	free(p);
-	return (as);
+	as->length += count;
+	as->s[as->length] = '\0';
+	return (defchar_used?-1:ret);
 }
 
-#else
+#elif defined(HAVE_WCSNRTOMBS)
 
 /*
  * Translates a wide character string into current locale character set
- * and appends to the archive_string.  Note: returns NULL if conversion
+ * and appends to the archive_string.  Note: returns -1 if conversion
  * fails.
- *
- * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion
- * one character at a time.  If a non-Windows platform doesn't have
- * either of these, fall back to the built-in UTF8 conversion.
  */
-struct archive_string *
-__archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w)
+int
+archive_string_append_from_wcs(struct archive_string *as,
+    const wchar_t *w, size_t len)
 {
-#if !defined(HAVE_WCTOMB) && !defined(HAVE_WCRTOMB)
-	/* If there's no built-in locale support, fall back to UTF8 always. */
-	return __archive_strappend_w_utf8(as, w);
-#else
+	mbstate_t shift_state;
+	size_t r, ndest, nwc;
+	char *dest;
+	const wchar_t *wp, *wpp;
+	int ret_val = 0;
+
+	wp = w;
+	nwc = len;
+	ndest = len * 2;
+	/* Initialize the shift state. */
+	memset(&shift_state, 0, sizeof(shift_state));
+	while (nwc > 0) {
+		/* Allocate buffer for MBS. */
+		if (archive_string_ensure(as, as->length + ndest + 1) == NULL)
+			__archive_errx(1, "Out of memory");
+
+		dest = as->s + as->length;
+		wpp = wp;
+		r = wcsnrtombs(dest, &wp, nwc,
+		    as->buffer_length - as->length -1,
+		    &shift_state);
+		if (r == (size_t)-1) {
+			if (errno == EILSEQ) {
+				/* Retry conversion just for safe WCS. */
+				size_t xwc = wp - wpp;
+				wp = wpp;
+				r = wcsnrtombs(dest, &wp, xwc,
+				    as->buffer_length - as->length -1,
+				    &shift_state);
+				if (r == (size_t)-1)
+					/* This would not happen. */
+					return (-1);
+				as->length += r;
+				nwc -= wp - wpp;
+				/* Skip an illegal wide char. */
+				as->s[as->length++] = '?';
+				wp++;
+				nwc--;
+				ret_val = -1;
+				continue;
+			} else {
+				ret_val = -1;
+				break;
+			}
+		}
+		as->length += r;
+		if (wp == NULL || (wp - wpp) >= (int64_t)nwc)
+			break;
+		/* Get a remaining WCS lenth. */
+		nwc -= wp - wpp;
+	}
+	/* All wide characters are translated to MBS. */
+	as->s[as->length] = '\0';
+	return (ret_val);
+}
+
+#elif defined(HAVE_WCTOMB) || defined(HAVE_WCRTOMB)
+
+/*
+ * Translates a wide character string into current locale character set
+ * and appends to the archive_string.  Note: returns -1 if conversion
+ * fails.
+ */
+int
+archive_string_append_from_wcs(struct archive_string *as,
+    const wchar_t *w, size_t len)
+{
 	/* We cannot use the standard wcstombs() here because it
 	 * cannot tell us how big the output buffer should be.  So
 	 * I've built a loop around wcrtomb() or wctomb() that
 	 * converts a character at a time and resizes the string as
 	 * needed.  We prefer wcrtomb() when it's available because
 	 * it's thread-safe. */
-	int n;
+	int n, ret_val = 0;
 	char *p;
-	char buff[256];
+	char *end;
 #if HAVE_WCRTOMB
 	mbstate_t shift_state;
 
@@ -421,33 +887,3351 @@
 	/* Clear the shift state before starting. */
 	wctomb(NULL, L'\0');
 #endif
-
 	/*
-	 * Convert one wide char at a time into 'buff', whenever that
-	 * fills, append it to the string.
+	 * Allocate buffer for MBS.
+	 * We need this allocation here since it is possible that
+	 * as->s is still NULL.
 	 */
-	p = buff;
-	while (*w != L'\0') {
-		/* Flush the buffer when we have <=16 bytes free. */
-		/* (No encoding has a single character >16 bytes.) */
-		if ((size_t)(p - buff) >= (size_t)(sizeof(buff) - MB_CUR_MAX)) {
-			*p = '\0';
-			archive_strcat(as, buff);
-			p = buff;
+	if (archive_string_ensure(as, as->length + len + 1) == NULL)
+		__archive_errx(1, "Out of memory");
+
+	p = as->s + as->length;
+	end = as->s + as->buffer_length - MB_CUR_MAX -1;
+	while (*w != L'\0' && len > 0) {
+		if (p >= end) {
+			as->length = p - as->s;
+			as->s[as->length] = '\0';
+			/* Re-allocate buffer for MBS. */
+			if (archive_string_ensure(as,
+			    as->length + len * 2 + 1) == NULL)
+				__archive_errx(1, "Out of memory");
+			p = as->s + as->length;
+			end = as->s + as->buffer_length - MB_CUR_MAX -1;
 		}
 #if HAVE_WCRTOMB
 		n = wcrtomb(p, *w++, &shift_state);
 #else
 		n = wctomb(p, *w++);
 #endif
+		if (n == -1) {
+			if (errno == EILSEQ) {
+				/* Skip an illegal wide char. */
+				*p++ = '?';
+				ret_val = -1;
+			} else {
+				ret_val = -1;
+				break;
+			}
+		} else
+			p += n;
+		len--;
+	}
+	as->length = p - as->s;
+	as->s[as->length] = '\0';
+	return (ret_val);
+}
+
+#else /* HAVE_WCTOMB || HAVE_WCRTOMB */
+
+/*
+ * TODO: Test if __STDC_ISO_10646__ is defined.
+ * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion
+ * one character at a time.  If a non-Windows platform doesn't have
+ * either of these, fall back to the built-in UTF8 conversion.
+ */
+int
+archive_string_append_from_wcs(struct archive_string *as,
+    const wchar_t *w, size_t len)
+{
+	(void)as;/* UNUSED */
+	(void)w;/* UNUSED */
+	(void)len;/* UNUSED */
+	return (-1);
+}
+
+#endif /* HAVE_WCTOMB || HAVE_WCRTOMB */
+
+/*
+ * Find a string conversion object by a pair of 'from' charset name
+ * and 'to' charset name from an archive object.
+ * Return NULL if not found.
+ */
+static struct archive_string_conv *
+find_sconv_object(struct archive *a, const char *fc, const char *tc)
+{
+	struct archive_string_conv *sc; 
+
+	if (a == NULL)
+		return (NULL);
+
+	for (sc = a->sconv; sc != NULL; sc = sc->next) {
+		if (strcmp(sc->from_charset, fc) == 0 &&
+		    strcmp(sc->to_charset, tc) == 0)
+			break;
+	}
+	return (sc);
+}
+
+/*
+ * Register a string object to an archive object.
+ */
+static void
+add_sconv_object(struct archive *a, struct archive_string_conv *sc)
+{
+	struct archive_string_conv **psc; 
+
+	/* Add a new sconv to sconv list. */
+	psc = &(a->sconv);
+	while (*psc != NULL)
+		psc = &((*psc)->next);
+	*psc = sc;
+}
+
+#if defined(__APPLE__)
+
+static int
+createUniInfo(struct archive_string_conv *sconv)
+{
+	UnicodeMapping map;
+	OSStatus err;
+
+	map.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault,
+	    kUnicodeNoSubset, kUnicode16BitFormat);
+	map.otherEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault,
+	    kUnicodeHFSPlusDecompVariant, kUnicode16BitFormat);
+	map.mappingVersion = kUnicodeUseLatestMapping;
+
+	sconv->uniInfo = NULL;
+	err = CreateUnicodeToTextInfo(&map, &(sconv->uniInfo));
+	return ((err == noErr)? 0: -1);
+}
+
+#endif /* __APPLE__ */
+
+static void
+add_converter(struct archive_string_conv *sc, int (*converter)
+    (struct archive_string *, const void *, size_t,
+     struct archive_string_conv *))
+{
+	if (sc == NULL || sc->nconverter >= 2)
+		__archive_errx(1, "Programing error");
+	sc->converter[sc->nconverter++] = converter;
+}
+
+static void
+setup_converter(struct archive_string_conv *sc)
+{
+
+	/* Reset. */
+	sc->nconverter = 0;
+
+	/*
+	 * Perform special sequence for the incorrect UTF-8 filenames
+	 * made by libarchive2.x.
+	 */
+	if (sc->flag & SCONV_UTF8_LIBARCHIVE_2) {
+		add_converter(sc, strncat_from_utf8_libarchive2);
+		return;
+	}
+
+	/*
+	 * Convert a string to UTF-16BE/LE.
+	 */
+	if (sc->flag & SCONV_TO_UTF16) {
+		/*
+		 * If the current locale is UTF-8, we can translate
+		 * a UTF-8 string into a UTF-16BE string.
+		 */
+		if (sc->flag & SCONV_FROM_UTF8) {
+			add_converter(sc, archive_string_append_unicode);
+			return;
+		}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		if (sc->flag & SCONV_WIN_CP) {
+			if (sc->flag & SCONV_TO_UTF16BE)
+				add_converter(sc, win_strncat_to_utf16be);
+			else
+				add_converter(sc, win_strncat_to_utf16le);
+			return;
+		}
+#endif
+
+#if defined(HAVE_ICONV)
+		if (sc->cd != (iconv_t)-1) {
+			add_converter(sc, iconv_strncat_in_locale);
+			return;
+		}
+#endif
+
+		if (sc->flag & SCONV_BEST_EFFORT) {
+			if (sc->flag & SCONV_TO_UTF16BE)
+				add_converter(sc, best_effort_strncat_to_utf16be);
+			else
+				add_converter(sc, best_effort_strncat_to_utf16le);
+		} else
+			/* Make sure we have no converter. */
+			sc->nconverter = 0;
+		return;
+	}
+
+	/*
+	 * Convert a string from UTF-16BE/LE.
+	 */
+	if (sc->flag & SCONV_FROM_UTF16) {
+		/*
+		 * At least we should normalize a UTF-16BE string.
+		 */
+#if defined(__APPLE__)
+		if (sc->flag & SCONV_NORMALIZATION_D)
+			add_converter(sc,archive_string_normalize_D);
+		else
+#endif
+		if (sc->flag & SCONV_NORMALIZATION_C)
+			add_converter(sc, archive_string_normalize_C);
+
+		if (sc->flag & SCONV_TO_UTF8) {
+			/*
+			 * If the current locale is UTF-8, we can translate
+			 * a UTF-16BE/LE string into a UTF-8 string directly.
+			 */
+			if (!(sc->flag &
+			    (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C)))
+				add_converter(sc,
+				    archive_string_append_unicode);
+			return;
+		}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		if (sc->flag & SCONV_WIN_CP) {
+			if (sc->flag & SCONV_FROM_UTF16BE)
+				add_converter(sc, win_strncat_from_utf16be);
+			else
+				add_converter(sc, win_strncat_from_utf16le);
+			return;
+		}
+#endif
+
+#if defined(HAVE_ICONV)
+		if (sc->cd != (iconv_t)-1) {
+			add_converter(sc, iconv_strncat_in_locale);
+			return;
+		}
+#endif
+
+		if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE))
+		    == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE))
+			add_converter(sc, best_effort_strncat_from_utf16be);
+		else if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE))
+		    == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE))
+			add_converter(sc, best_effort_strncat_from_utf16le);
+		else
+			/* Make sure we have no converter. */
+			sc->nconverter = 0;
+		return;
+	}
+
+	if (sc->flag & SCONV_FROM_UTF8) {
+		/*
+		 * At least we should normalize a UTF-8 string.
+		 */
+#if defined(__APPLE__)
+		if (sc->flag & SCONV_NORMALIZATION_D)
+			add_converter(sc,archive_string_normalize_D);
+		else
+#endif
+		if (sc->flag & SCONV_NORMALIZATION_C)
+			add_converter(sc, archive_string_normalize_C);
+
+		/*
+		 * Copy UTF-8 string with a check of CESU-8.
+		 * Apparently, iconv does not check surrogate pairs in UTF-8
+		 * when both from-charset and to-charset are UTF-8, and then
+		 * we use our UTF-8 copy code.
+		 */
+		if (sc->flag & SCONV_TO_UTF8) {
+			/*
+			 * If the current locale is UTF-8, we can translate
+			 * a UTF-16BE string into a UTF-8 string directly.
+			 */
+			if (!(sc->flag &
+			    (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C)))
+				add_converter(sc, strncat_from_utf8_to_utf8);
+			return;
+		}
+	}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/*
+	 * On Windows we can use Windows API for a string conversion.
+	 */
+	if (sc->flag & SCONV_WIN_CP) {
+		add_converter(sc, strncat_in_codepage);
+		return;
+	}
+#endif
+
+#if HAVE_ICONV
+	if (sc->cd != (iconv_t)-1) {
+		add_converter(sc, iconv_strncat_in_locale);
+		return;
+	}
+#endif
+
+	/*
+	 * Try conversion in the best effort or no conversion.
+	 */
+	if ((sc->flag & SCONV_BEST_EFFORT) || sc->same)
+		add_converter(sc, best_effort_strncat_in_locale);
+	else
+		/* Make sure we have no converter. */
+		sc->nconverter = 0;
+}
+
+/*
+ * Return canonicalized charset-name but this supports just UTF-8, UTF-16BE
+ * and CP932 which are referenced in create_sconv_object().
+ */
+static const char *
+canonical_charset_name(const char *charset)
+{
+	char cs[16];
+	char *p;
+	const char *s;
+
+	if (charset == NULL || charset[0] == '\0'
+	    || strlen(charset) > 15)
+		return (charset);
+
+	/* Copy name to uppercase. */
+	p = cs;
+	s = charset;
+	while (*s) {
+		char c = *s++;
+		if (c >= 'a' && c <= 'z')
+			c -= 'a' - 'A';
+		*p++ = c;
+	}
+	*p++ = '\0';
+
+	if (strcmp(cs, "UTF-8") == 0 ||
+	    strcmp(cs, "UTF8") == 0)
+		return ("UTF-8");
+	if (strcmp(cs, "UTF-16BE") == 0 ||
+	    strcmp(cs, "UTF16BE") == 0)
+		return ("UTF-16BE");
+	if (strcmp(cs, "UTF-16LE") == 0 ||
+	    strcmp(cs, "UTF16LE") == 0)
+		return ("UTF-16LE");
+	if (strcmp(cs, "CP932") == 0)
+		return ("CP932");
+	return (charset);
+}
+
+/*
+ * Create a string conversion object.
+ */
+static struct archive_string_conv *
+create_sconv_object(const char *fc, const char *tc,
+    unsigned current_codepage, int flag)
+{
+	struct archive_string_conv *sc; 
+
+	sc = calloc(1, sizeof(*sc));
+	if (sc == NULL)
+		return (NULL);
+	sc->next = NULL;
+	sc->from_charset = strdup(fc);
+	if (sc->from_charset == NULL) {
+		free(sc);
+		return (NULL);
+	}
+	sc->to_charset = strdup(tc);
+	if (sc->to_charset == NULL) {
+		free(sc);
+		free(sc->from_charset);
+		return (NULL);
+	}
+	archive_string_init(&sc->utftmp);
+#if defined(__APPLE__)
+	archive_string_init(&sc->utf16nfc);
+	archive_string_init(&sc->utf16nfd);
+#endif
+
+	if (flag & SCONV_TO_CHARSET) {
+		/*
+		 * Convert characters from the current locale charset to
+		 * a specified charset.
+		 */
+		sc->from_cp = current_codepage;
+		sc->to_cp = make_codepage_from_charset(tc);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		if (IsValidCodePage(sc->to_cp))
+			flag |= SCONV_WIN_CP;
+#endif
+	} else if (flag & SCONV_FROM_CHARSET) {
+		/*
+		 * Convert characters from a specified charset to
+		 * the current locale charset.
+		 */
+		sc->to_cp = current_codepage;
+		sc->from_cp = make_codepage_from_charset(fc);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		if (IsValidCodePage(sc->from_cp))
+			flag |= SCONV_WIN_CP;
+#endif
+	}
+
+	/*
+	 * Check if "from charset" and "to charset" are the same.
+	 */
+	if (strcmp(fc, tc) == 0 ||
+	    (sc->from_cp != (unsigned)-1 && sc->from_cp == sc->to_cp))
+		sc->same = 1;
+	else
+		sc->same = 0;
+
+	/*
+	 * Mark if "from charset" or "to charset" are UTF-8 or UTF-16BE/LE.
+	 */
+	if (strcmp(tc, "UTF-8") == 0)
+		flag |= SCONV_TO_UTF8;
+	else if (strcmp(tc, "UTF-16BE") == 0)
+		flag |= SCONV_TO_UTF16BE;
+	else if (strcmp(tc, "UTF-16LE") == 0)
+		flag |= SCONV_TO_UTF16LE;
+	if (strcmp(fc, "UTF-8") == 0)
+		flag |= SCONV_FROM_UTF8;
+	else if (strcmp(fc, "UTF-16BE") == 0)
+		flag |= SCONV_FROM_UTF16BE;
+	else if (strcmp(fc, "UTF-16LE") == 0)
+		flag |= SCONV_FROM_UTF16LE;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	if (sc->to_cp == CP_UTF8)
+		flag |= SCONV_TO_UTF8;
+	else if (sc->to_cp == CP_UTF16BE)
+		flag |= SCONV_TO_UTF16BE | SCONV_WIN_CP;
+	else if (sc->to_cp == CP_UTF16LE)
+		flag |= SCONV_TO_UTF16LE | SCONV_WIN_CP;
+	if (sc->from_cp == CP_UTF8)
+		flag |= SCONV_FROM_UTF8;
+	else if (sc->from_cp == CP_UTF16BE)
+		flag |= SCONV_FROM_UTF16BE | SCONV_WIN_CP;
+	else if (sc->from_cp == CP_UTF16LE)
+		flag |= SCONV_FROM_UTF16LE | SCONV_WIN_CP;
+#endif
+
+	/*
+	 * Set a flag for Unicode NFD. Usually iconv cannot correctly
+	 * handle it. So we have to translate NFD characters to NFC ones
+	 * ourselves before iconv handles. Another reason is to prevent
+	 * that the same sight of two filenames, one is NFC and other
+	 * is NFD, would be in its directory.
+	 * On Mac OS X, although its filesystem layer automatically
+	 * convert filenames to NFD, it would be useful for filename
+	 * comparing to find out the same filenames that we normalize
+	 * that to be NFD ourselves.
+	 */
+	if ((flag & SCONV_FROM_CHARSET) &&
+	    (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8))) {
+#if defined(__APPLE__)
+		if (flag & SCONV_TO_UTF8) {
+			if (createUniInfo(sc) == 0)
+				flag |= SCONV_NORMALIZATION_D;
+		} else
+#endif
+			flag |= SCONV_NORMALIZATION_C;
+	}
+
+#if defined(HAVE_ICONV)
+	sc->cd_w = (iconv_t)-1;
+	/*
+	 * Create an iconv object.
+	 */
+	if (((flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) &&
+	    (flag & (SCONV_FROM_UTF8 | SCONV_FROM_UTF16))) ||
+	    (flag & SCONV_WIN_CP)) {
+		/* This case we won't use iconv. */
+		sc->cd = (iconv_t)-1;
+#if defined(__APPLE__)
+	} else if ((flag & SCONV_FROM_CHARSET) && (flag & SCONV_TO_UTF8)) {
+		/*
+		 * In case reading an archive file.
+		 * Translate non-Unicode filenames in an archive file to
+		 * UTF-8-MAC filenames.
+		 */
+		sc->cd = iconv_open("UTF-8-MAC", fc);
+		if (sc->cd == (iconv_t)-1) {
+			if ((sc->flag & SCONV_BEST_EFFORT) &&
+			    strcmp(fc, "CP932") == 0) {
+				sc->cd = iconv_open("UTF-8-MAC", "SJIS");
+				if (sc->cd == (iconv_t)-1) {
+					sc->cd = iconv_open(tc, fc);
+					if (sc->cd == (iconv_t)-1)
+						sc->cd = iconv_open(tc, "SJIS");
+				}
+			} else
+				sc->cd = iconv_open(tc, fc);
+		}
+	} else if ((flag & SCONV_TO_CHARSET) && (flag & SCONV_FROM_UTF8)) {
+		/*
+		 * In case writing an archive file.
+		 * Translate UTF-8-MAC filenames in HFS Plus to non-Unicode
+		 * filenames.
+		 */
+		sc->cd = iconv_open(tc, "UTF-8-MAC");
+		if (sc->cd == (iconv_t)-1) {
+			if ((sc->flag & SCONV_BEST_EFFORT) &&
+			    strcmp(tc, "CP932") == 0) {
+				sc->cd = iconv_open("SJIS", "UTF-8-MAC");
+				if (sc->cd == (iconv_t)-1) {
+					sc->cd = iconv_open(tc, fc);
+					if (sc->cd == (iconv_t)-1)
+						sc->cd = iconv_open("SJIS", fc);
+				}
+			} else
+				sc->cd = iconv_open(tc, fc);
+		}
+#endif
+	} else {
+		sc->cd = iconv_open(tc, fc);
+		if (sc->cd == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) {
+			/*
+			 * Unfortunaly, all of iconv implements do support 
+			 * "CP932" character-set, so we should use "SJIS"
+			 * instead if iconv_open failed.
+			 */
+			if (strcmp(tc, "CP932") == 0)
+				sc->cd = iconv_open("SJIS", fc);
+			else if (strcmp(fc, "CP932") == 0)
+				sc->cd = iconv_open(tc, "SJIS");
+		}
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		/*
+		 * archive_mstring on Windows directly convert multi-bytes
+		 * into archive_wstring in order not to depend on locale
+		 * so that you can do a I18N programing. This will be
+		 * used only in archive_mstring_copy_mbs_len_l so far.
+		 */
+		if (flag & SCONV_FROM_CHARSET) {
+			sc->cd_w = iconv_open("UTF-8", fc);
+			if (sc->cd_w == (iconv_t)-1 &&
+			    (sc->flag & SCONV_BEST_EFFORT)) {
+				if (strcmp(fc, "CP932") == 0)
+					sc->cd_w = iconv_open("UTF-8", "SJIS");
+			}
+		}
+#endif /* _WIN32 && !__CYGWIN__ */
+	}
+#endif	/* HAVE_ICONV */
+
+	sc->flag = flag;
+
+	/*
+	 * Setup converters.
+	 */
+	setup_converter(sc);
+
+	return (sc);
+}
+
+/*
+ * Free a string conversion object.
+ */
+static void
+free_sconv_object(struct archive_string_conv *sc)
+{
+	free(sc->from_charset);
+	free(sc->to_charset);
+	archive_string_free(&sc->utftmp);
+#if HAVE_ICONV
+	if (sc->cd != (iconv_t)-1)
+		iconv_close(sc->cd);
+	if (sc->cd_w != (iconv_t)-1)
+		iconv_close(sc->cd_w);
+#endif
+#if defined(__APPLE__)
+	archive_string_free(&sc->utf16nfc);
+	archive_string_free(&sc->utf16nfd);
+	if (sc->uniInfo != NULL)
+		DisposeUnicodeToTextInfo(&(sc->uniInfo));
+#endif
+	free(sc);
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+static unsigned
+my_atoi(const char *p)
+{
+	unsigned cp;
+
+	cp = 0;
+	while (*p) {
+		if (*p >= '0' && *p <= '9')
+			cp = cp * 10 + (*p - '0');
+		else
+			return (-1);
+		p++;
+	}
+	return (cp);
+}
+
+/*
+ * Translate Charset name (as used by iconv) into CodePage (as used by Windows)
+ * Return -1 if failed.
+ *
+ * Note: This translation code may be insufficient.
+ */
+static struct charset {
+	const char *name;
+	unsigned cp;
+} charsets[] = {
+	/* MUST BE SORTED! */
+	{"ASCII", 1252},
+	{"ASMO-708", 708},
+	{"BIG5", 950},
+	{"CHINESE", 936},
+	{"CP367", 1252},
+	{"CP819", 1252},
+	{"CP1025", 21025},
+	{"DOS-720", 720},
+	{"DOS-862", 862},
+	{"EUC-CN", 51936},
+	{"EUC-JP", 51932},
+	{"EUC-KR", 949},
+	{"EUCCN", 51936},
+	{"EUCJP", 51932},
+	{"EUCKR", 949},
+	{"GB18030", 54936},
+	{"GB2312", 936},
+	{"HEBREW", 1255},
+	{"HZ-GB-2312", 52936},
+	{"IBM273", 20273},
+	{"IBM277", 20277},
+	{"IBM278", 20278},
+	{"IBM280", 20280},
+	{"IBM284", 20284},
+	{"IBM285", 20285},
+	{"IBM290", 20290},
+	{"IBM297", 20297},
+	{"IBM367", 1252},
+	{"IBM420", 20420},
+	{"IBM423", 20423},
+	{"IBM424", 20424},
+	{"IBM819", 1252},
+	{"IBM871", 20871},
+	{"IBM880", 20880},
+	{"IBM905", 20905},
+	{"IBM924", 20924},
+	{"ISO-8859-1", 28591},
+	{"ISO-8859-13", 28603},
+	{"ISO-8859-15", 28605},
+	{"ISO-8859-2", 28592},
+	{"ISO-8859-3", 28593},
+	{"ISO-8859-4", 28594},
+	{"ISO-8859-5", 28595},
+	{"ISO-8859-6", 28596},
+	{"ISO-8859-7", 28597},
+	{"ISO-8859-8", 28598},
+	{"ISO-8859-9", 28599},
+	{"ISO8859-1", 28591},
+	{"ISO8859-13", 28603},
+	{"ISO8859-15", 28605},
+	{"ISO8859-2", 28592},
+	{"ISO8859-3", 28593},
+	{"ISO8859-4", 28594},
+	{"ISO8859-5", 28595},
+	{"ISO8859-6", 28596},
+	{"ISO8859-7", 28597},
+	{"ISO8859-8", 28598},
+	{"ISO8859-9", 28599},
+	{"JOHAB", 1361},
+	{"KOI8-R", 20866},
+	{"KOI8-U", 21866},
+	{"KS_C_5601-1987", 949},
+	{"LATIN1", 1252},
+	{"LATIN2", 28592},
+	{"MACINTOSH", 10000},
+	{"SHIFT-JIS", 932},
+	{"SHIFT_JIS", 932},
+	{"SJIS", 932},
+	{"US", 1252},
+	{"US-ASCII", 1252},
+	{"UTF-16", 1200},
+	{"UTF-16BE", 1201},
+	{"UTF-16LE", 1200},
+	{"UTF-8", CP_UTF8},
+	{"X-EUROPA", 29001},
+	{"X-MAC-ARABIC", 10004},
+	{"X-MAC-CE", 10029},
+	{"X-MAC-CHINESEIMP", 10008},
+	{"X-MAC-CHINESETRAD", 10002},
+	{"X-MAC-CROATIAN", 10082},
+	{"X-MAC-CYRILLIC", 10007},
+	{"X-MAC-GREEK", 10006},
+	{"X-MAC-HEBREW", 10005},
+	{"X-MAC-ICELANDIC", 10079},
+	{"X-MAC-JAPANESE", 10001},
+	{"X-MAC-KOREAN", 10003},
+	{"X-MAC-ROMANIAN", 10010},
+	{"X-MAC-THAI", 10021},
+	{"X-MAC-TURKISH", 10081},
+	{"X-MAC-UKRAINIAN", 10017},
+};
+static unsigned
+make_codepage_from_charset(const char *charset)
+{
+	char cs[16];
+	char *p;
+	unsigned cp;
+	int a, b;
+
+	if (charset == NULL || strlen(charset) > 15)
+		return -1;
+
+	/* Copy name to uppercase. */
+	p = cs;
+	while (*charset) {
+		char c = *charset++;
+		if (c >= 'a' && c <= 'z')
+			c -= 'a' - 'A';
+		*p++ = c;
+	}
+	*p++ = '\0';
+	cp = -1;
+
+	/* Look it up in the table first, so that we can easily
+	 * override CP367, which we map to 1252 instead of 367. */
+	a = 0;
+	b = sizeof(charsets)/sizeof(charsets[0]);
+	while (b > a) {
+		int c = (b + a) / 2;
+		int r = strcmp(charsets[c].name, cs);
+		if (r < 0)
+			a = c + 1;
+		else if (r > 0)
+			b = c;
+		else
+			return charsets[c].cp;
+	}
+
+	/* If it's not in the table, try to parse it. */
+	switch (*cs) {
+	case 'C':
+		if (cs[1] == 'P' && cs[2] >= '0' && cs[2] <= '9') {
+			cp = my_atoi(cs + 2);
+		} else if (strcmp(cs, "CP_ACP") == 0)
+			cp = get_current_codepage();
+		else if (strcmp(cs, "CP_OEMCP") == 0)
+			cp = get_current_oemcp();
+		break;
+	case 'I':
+		if (cs[1] == 'B' && cs[2] == 'M' &&
+		    cs[3] >= '0' && cs[3] <= '9') {
+			cp = my_atoi(cs + 3);
+		}
+		break;
+	case 'W':
+		if (strncmp(cs, "WINDOWS-", 8) == 0) {
+			cp = my_atoi(cs + 8);
+			if (cp != 874 && (cp < 1250 || cp > 1258))
+				cp = -1;/* This may invalid code. */
+		}
+		break;
+	}
+	return (cp);
+}
+
+/*
+ * Return ANSI Code Page of current locale set by setlocale().
+ */
+static unsigned
+get_current_codepage(void)
+{
+	char *locale, *p;
+	unsigned cp;
+
+	locale = setlocale(LC_CTYPE, NULL);
+	if (locale == NULL)
+		return (GetACP());
+	if (locale[0] == 'C' && locale[1] == '\0')
+		return (CP_C_LOCALE);
+	p = strrchr(locale, '.');
+	if (p == NULL)
+		return (GetACP());
+	cp = my_atoi(p+1);
+	if (cp <= 0)
+		return (GetACP());
+	return (cp);
+}
+
+/*
+ * Translation table between Locale Name and ACP/OEMCP.
+ */
+static struct {
+	unsigned acp;
+	unsigned ocp;
+	const char *locale;
+} acp_ocp_map[] = {
+	{  950,  950, "Chinese_Taiwan" },
+	{  936,  936, "Chinese_People's Republic of China" },
+	{  950,  950, "Chinese_Taiwan" },
+	{ 1250,  852, "Czech_Czech Republic" },
+	{ 1252,  850, "Danish_Denmark" },
+	{ 1252,  850, "Dutch_Netherlands" },
+	{ 1252,  850, "Dutch_Belgium" },
+	{ 1252,  437, "English_United States" },
+	{ 1252,  850, "English_Australia" },
+	{ 1252,  850, "English_Canada" },
+	{ 1252,  850, "English_New Zealand" },
+	{ 1252,  850, "English_United Kingdom" },
+	{ 1252,  437, "English_United States" },
+	{ 1252,  850, "Finnish_Finland" },
+	{ 1252,  850, "French_France" },
+	{ 1252,  850, "French_Belgium" },
+	{ 1252,  850, "French_Canada" },
+	{ 1252,  850, "French_Switzerland" },
+	{ 1252,  850, "German_Germany" },
+	{ 1252,  850, "German_Austria" },
+	{ 1252,  850, "German_Switzerland" },
+	{ 1253,  737, "Greek_Greece" },
+	{ 1250,  852, "Hungarian_Hungary" },
+	{ 1252,  850, "Icelandic_Iceland" },
+	{ 1252,  850, "Italian_Italy" },
+	{ 1252,  850, "Italian_Switzerland" },
+	{  932,  932, "Japanese_Japan" },
+	{  949,  949, "Korean_Korea" },
+	{ 1252,  850, "Norwegian (BokmOl)_Norway" },
+	{ 1252,  850, "Norwegian (BokmOl)_Norway" },
+	{ 1252,  850, "Norwegian-Nynorsk_Norway" },
+	{ 1250,  852, "Polish_Poland" },
+	{ 1252,  850, "Portuguese_Portugal" },
+	{ 1252,  850, "Portuguese_Brazil" },
+	{ 1251,  866, "Russian_Russia" },
+	{ 1250,  852, "Slovak_Slovakia" },
+	{ 1252,  850, "Spanish_Spain" },
+	{ 1252,  850, "Spanish_Mexico" },
+	{ 1252,  850, "Spanish_Spain" },
+	{ 1252,  850, "Swedish_Sweden" },
+	{ 1254,  857, "Turkish_Turkey" },
+	{ 0, 0, NULL}
+};
+
+/*
+ * Return OEM Code Page of current locale set by setlocale().
+ */
+static unsigned
+get_current_oemcp(void)
+{
+	int i;
+	char *locale, *p;
+	size_t len;
+
+	locale = setlocale(LC_CTYPE, NULL);
+	if (locale == NULL)
+		return (GetOEMCP());
+	if (locale[0] == 'C' && locale[1] == '\0')
+		return (CP_C_LOCALE);
+
+	p = strrchr(locale, '.');
+	if (p == NULL)
+		return (GetOEMCP());
+	len = p - locale;
+	for (i = 0; acp_ocp_map[i].acp; i++) {
+		if (strncmp(acp_ocp_map[i].locale, locale, len) == 0)
+			return (acp_ocp_map[i].ocp);
+	}
+	return (GetOEMCP());
+}
+#else
+
+/*
+ * POSIX platform does not use CodePage.
+ */
+
+static unsigned
+get_current_codepage(void)
+{
+	return (-1);/* Unknown */
+}
+static unsigned
+make_codepage_from_charset(const char *charset)
+{
+	(void)charset; /* UNUSED */
+	return (-1);/* Unknown */
+}
+static unsigned
+get_current_oemcp(void)
+{
+	return (-1);/* Unknown */
+}
+
+#endif /* defined(_WIN32) && !defined(__CYGWIN__) */
+
+/*
+ * Return a string conversion object.
+ */
+static struct archive_string_conv *
+get_sconv_object(struct archive *a, const char *fc, const char *tc, int flag)
+{
+	struct archive_string_conv *sc;
+	unsigned current_codepage;
+
+	/* Check if we have made the sconv object. */
+	sc = find_sconv_object(a, fc, tc);
+	if (sc != NULL)
+		return (sc);
+
+	if (a == NULL)
+		current_codepage = get_current_codepage();
+	else
+		current_codepage = a->current_codepage;
+
+	sc = create_sconv_object(canonical_charset_name(fc),
+	    canonical_charset_name(tc), current_codepage, flag);
+	if (sc == NULL) {
+		if (a != NULL)
+			archive_set_error(a, ENOMEM,
+			    "Could not allocate memory for "
+			    "a string conversion object");
+		return (NULL);
+	}
+
+	/*
+	 * If there is no converter for current string conversion object,
+	 * we cannot handle this conversion.
+	 */
+	if (sc->nconverter == 0) {
+		if (a != NULL) {
+#if HAVE_ICONV
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "iconv_open failed : Cannot handle ``%s''",
+			    (flag & SCONV_TO_CHARSET)?tc:fc);
+#else
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "A character-set conversion not fully supported "
+			    "on this platform");
+#endif
+		}
+		/* Failed; free a sconv object. */
+		free_sconv_object(sc);
+		return (NULL);
+	}
+
+	/*
+	 * Success!
+	 */
+	if (a != NULL)
+		add_sconv_object(a, sc);
+	return (sc);
+}
+
+static const char *
+get_current_charset(struct archive *a)
+{
+	const char *cur_charset;
+
+	if (a == NULL)
+		cur_charset = default_iconv_charset("");
+	else {
+		cur_charset = default_iconv_charset(a->current_code);
+		if (a->current_code == NULL) {
+			a->current_code = strdup(cur_charset);
+			a->current_codepage = get_current_codepage();
+			a->current_oemcp = get_current_oemcp();
+		}
+	}
+	return (cur_charset);
+}
+
+/*
+ * Make and Return a string conversion object.
+ * Return NULL if the platform does not support the specified conversion
+ * and best_effort is 0.
+ * If best_effort is set, A string conversion object must be returned
+ * unless memory allocation for the object fails, but the conversion
+ * might fail when non-ASCII code is found.
+ */
+struct archive_string_conv *
+archive_string_conversion_to_charset(struct archive *a, const char *charset,
+    int best_effort)
+{
+	int flag = SCONV_TO_CHARSET;
+
+	if (best_effort)
+		flag |= SCONV_BEST_EFFORT;
+	return (get_sconv_object(a, get_current_charset(a), charset, flag));
+}
+
+struct archive_string_conv *
+archive_string_conversion_from_charset(struct archive *a, const char *charset,
+    int best_effort)
+{
+	int flag = SCONV_FROM_CHARSET;
+
+	if (best_effort)
+		flag |= SCONV_BEST_EFFORT;
+	return (get_sconv_object(a, charset, get_current_charset(a), flag));
+}
+
+/*
+ * archive_string_default_conversion_*_archive() are provided for Windows
+ * platform because other archiver application use CP_OEMCP for
+ * MultiByteToWideChar() and WideCharToMultiByte() for the filenames
+ * in tar or zip files. But mbstowcs/wcstombs(CRT) usually use CP_ACP
+ * unless you use setlocale(LC_ALL, ".OCP")(specify CP_OEMCP).
+ * So we should make a string conversion between CP_ACP and CP_OEMCP
+ * for compatibillty.
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+struct archive_string_conv *
+archive_string_default_conversion_for_read(struct archive *a)
+{
+	const char *cur_charset = get_current_charset(a);
+	char oemcp[16];
+
+	/* NOTE: a check of cur_charset is unneeded but we need
+	 * that get_current_charset() has been surely called at
+	 * this time whatever C compiler optimized. */
+	if (cur_charset != NULL &&
+	    (a->current_codepage == CP_C_LOCALE ||
+	     a->current_codepage == a->current_oemcp))
+		return (NULL);/* no conversion. */
+
+	_snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);
+	/* Make sure a null termination must be set. */
+	oemcp[sizeof(oemcp)-1] = '\0';
+	return (get_sconv_object(a, oemcp, cur_charset,
+	    SCONV_FROM_CHARSET));
+}
+
+struct archive_string_conv *
+archive_string_default_conversion_for_write(struct archive *a)
+{
+	const char *cur_charset = get_current_charset(a);
+	char oemcp[16];
+
+	/* NOTE: a check of cur_charset is unneeded but we need
+	 * that get_current_charset() has been surely called at
+	 * this time whatever C compiler optimized. */
+	if (cur_charset != NULL &&
+	    (a->current_codepage == CP_C_LOCALE ||
+	     a->current_codepage == a->current_oemcp))
+		return (NULL);/* no conversion. */
+
+	_snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);
+	/* Make sure a null termination must be set. */
+	oemcp[sizeof(oemcp)-1] = '\0';
+	return (get_sconv_object(a, cur_charset, oemcp,
+	    SCONV_TO_CHARSET));
+}
+#else
+struct archive_string_conv *
+archive_string_default_conversion_for_read(struct archive *a)
+{
+	(void)a; /* UNUSED */
+	return (NULL);
+}
+
+struct archive_string_conv *
+archive_string_default_conversion_for_write(struct archive *a)
+{
+	(void)a; /* UNUSED */
+	return (NULL);
+}
+#endif
+
+/*
+ * Dispose of all character conversion objects in the archive object.
+ */
+void
+archive_string_conversion_free(struct archive *a)
+{
+	struct archive_string_conv *sc; 
+	struct archive_string_conv *sc_next; 
+
+	for (sc = a->sconv; sc != NULL; sc = sc_next) {
+		sc_next = sc->next;
+		free_sconv_object(sc);
+	}
+	a->sconv = NULL;
+	free(a->current_code);
+	a->current_code = NULL;
+}
+
+/*
+ * Return a conversion charset name.
+ */
+const char *
+archive_string_conversion_charset_name(struct archive_string_conv *sc)
+{
+	if (sc->flag & SCONV_TO_CHARSET)
+		return (sc->to_charset);
+	else
+		return (sc->from_charset);
+}
+
+/*
+ * Change the behavior of a string conversion.
+ */
+void
+archive_string_conversion_set_opt(struct archive_string_conv *sc, int opt)
+{
+	switch (opt) {
+	/*
+	 * A filename in UTF-8 was made with libarchive 2.x in a wrong
+	 * assumption that wchar_t was Unicode.
+	 * This option enables simulating the assumption in order to read
+	 * that filname correctly.
+	 */
+	case SCONV_SET_OPT_UTF8_LIBARCHIVE2X:
+#if (defined(_WIN32) && !defined(__CYGWIN__)) \
+	 || defined(__STDC_ISO_10646__) || defined(__APPLE__)
+		/*
+		 * Nothing to do for it since wchar_t on these platforms
+		 * is really Unicode.
+		 */
+		(void)sc; /* UNUSED */
+#else
+		if ((sc->flag & SCONV_UTF8_LIBARCHIVE_2) == 0) {
+			sc->flag |= SCONV_UTF8_LIBARCHIVE_2;
+			/* Re-setup string converters. */
+			setup_converter(sc);
+		}
+#endif
+		break;
+	default:
+		break;
+	}
+}
+
+/*
+ *
+ * Copy one archive_string to another in locale conversion.
+ *
+ *	archive_strncpy_in_locale();
+ *	archive_strcpy_in_locale();
+ *
+ */
+
+static size_t
+mbsnbytes(const void *_p, size_t n)
+{
+	size_t s;
+	const char *p, *pp;
+
+	if (_p == NULL)
+		return (0);
+	p = (const char *)_p;
+
+	/* Like strlen(p), except won't examine positions beyond p[n]. */
+	s = 0;
+	pp = p;
+	while (s < n && *pp) {
+		pp++;
+		s++;
+	}
+	return (s);
+}
+
+static size_t
+utf16nbytes(const void *_p, size_t n)
+{
+	size_t s;
+	const char *p, *pp;
+
+	if (_p == NULL)
+		return (0);
+	p = (const char *)_p;
+
+	/* Like strlen(p), except won't examine positions beyond p[n]. */
+	s = 0;
+	pp = p;
+	n >>= 1;
+	while (s < n && (pp[0] || pp[1])) {
+		pp += 2;
+		s++;
+	}
+	return (s<<1);
+}
+
+int
+archive_strncpy_in_locale(struct archive_string *as, const void *_p, size_t n,
+    struct archive_string_conv *sc)
+{
+	as->length = 0;
+	return (archive_strncat_in_locale(as, _p, n, sc));
+}
+
+int
+archive_strncat_in_locale(struct archive_string *as, const void *_p, size_t n,
+    struct archive_string_conv *sc)
+{
+	const void *s;
+	size_t length;
+	int i, r = 0, r2;
+
+	/* We must allocate memory even if there is no data for conversion
+	 * or copy. This simulates archive_string_append behavior. */
+	if (_p == NULL || n == 0) {
+		int tn = 1;
+		if (sc != NULL && (sc->flag & SCONV_TO_UTF16))
+			tn = 2;
+		if (archive_string_ensure(as, as->length + tn) == NULL)
+			return (-1);
+		as->s[as->length] = 0;
+		if (tn == 2)
+			as->s[as->length+1] = 0;
+		return (0);
+	}
+
+	/*
+	 * If sc is NULL, we just make a copy.
+	 */
+	if (sc == NULL) {
+		length = mbsnbytes(_p, n);
+		if (archive_string_append(as, _p, length) == NULL)
+			return (-1);/* No memory */
+		return (0);
+	}
+
+	if (sc->flag & SCONV_FROM_UTF16)
+		length = utf16nbytes(_p, n);
+	else
+		length = mbsnbytes(_p, n);
+	s = _p;
+	i = 0;
+	if (sc->nconverter > 1) {
+		sc->utftmp.length = 0;
+		r2 = sc->converter[0](&(sc->utftmp), s, length, sc);
+		if (r2 != 0 && errno == ENOMEM)
+			return (r2);
+		if (r > r2)
+			r = r2;
+		s = sc->utftmp.s;
+		length = sc->utftmp.length;
+		++i;
+	}
+	r2 = sc->converter[i](as, s, length, sc);
+	if (r > r2)
+		r = r2;
+	return (r);
+}
+
+#if HAVE_ICONV
+
+/*
+ * Return -1 if conversion failes.
+ */
+static int
+iconv_strncat_in_locale(struct archive_string *as, const void *_p,
+    size_t length, struct archive_string_conv *sc)
+{
+	ICONV_CONST char *inp;
+	size_t remaining;
+	iconv_t cd;
+	char *outp;
+	size_t avail, bs;
+	int return_value = 0; /* success */
+	int to_size, from_size;
+
+	if (sc->flag & SCONV_TO_UTF16)
+		to_size = 2;
+	else
+		to_size = 1;
+	if (sc->flag & SCONV_FROM_UTF16)
+		from_size = 2;
+	else
+		from_size = 1;
+
+	if (archive_string_ensure(as, as->length + length*2+to_size) == NULL)
+		return (-1);
+
+	cd = sc->cd;
+	inp = (char *)(uintptr_t)_p;
+	remaining = length;
+	outp = as->s + as->length;
+	avail = as->buffer_length - as->length - to_size;
+	while (remaining >= (size_t)from_size) {
+		size_t result = iconv(cd, &inp, &remaining, &outp, &avail);
+
+		if (result != (size_t)-1)
+			break; /* Conversion completed. */
+
+		if (errno == EILSEQ || errno == EINVAL) {
+			/*
+		 	 * If an output charset is UTF-8 or UTF-16BE/LE,
+			 * unknown character should be U+FFFD
+			 * (replacement character).
+			 */
+			if (sc->flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) {
+				size_t rbytes;
+				if (sc->flag & SCONV_TO_UTF8)
+					rbytes = UTF8_R_CHAR_SIZE;
+				else
+					rbytes = 2;
+
+				if (avail < rbytes) {
+					as->length = outp - as->s;
+					bs = as->buffer_length +
+					    (remaining * to_size) + rbytes;
+					if (NULL ==
+					    archive_string_ensure(as, bs))
+						return (-1);
+					outp = as->s + as->length;
+					avail = as->buffer_length
+					    - as->length - to_size;
+				}
+				if (sc->flag & SCONV_TO_UTF8)
+					UTF8_SET_R_CHAR(outp);
+				else if (sc->flag & SCONV_TO_UTF16BE)
+					archive_be16enc(outp, UNICODE_R_CHAR);
+				else
+					archive_le16enc(outp, UNICODE_R_CHAR);
+				outp += rbytes;
+				avail -= rbytes;
+			} else {
+				/* Skip the illegal input bytes. */
+				*outp++ = '?';
+				avail--;
+			}
+			inp += from_size;
+			remaining -= from_size;
+			return_value = -1; /* failure */
+		} else {
+			/* E2BIG no output buffer,
+			 * Increase an output buffer.  */
+			as->length = outp - as->s;
+			bs = as->buffer_length + remaining * 2;
+			if (NULL == archive_string_ensure(as, bs))
+				return (-1);
+			outp = as->s + as->length;
+			avail = as->buffer_length - as->length - to_size;
+		}
+	}
+	as->length = outp - as->s;
+	as->s[as->length] = 0;
+	if (to_size == 2)
+		as->s[as->length+1] = 0;
+	return (return_value);
+}
+
+#endif /* HAVE_ICONV */
+
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Translate a string from a some CodePage to an another CodePage by
+ * Windows APIs, and copy the result. Return -1 if conversion failes.
+ */
+static int
+strncat_in_codepage(struct archive_string *as,
+    const void *_p, size_t length, struct archive_string_conv *sc)
+{
+	const char *s = (const char *)_p;
+	struct archive_wstring aws;
+	size_t l;
+	int r, saved_flag;
+
+	archive_string_init(&aws);
+	saved_flag = sc->flag;
+	sc->flag &= ~(SCONV_NORMALIZATION_D | SCONV_NORMALIZATION_C);
+	r = archive_wstring_append_from_mbs_in_codepage(&aws, s, length, sc);
+	sc->flag = saved_flag;
+	if (r != 0) {
+		archive_wstring_free(&aws);
+		if (errno != ENOMEM)
+			archive_string_append(as, s, length);
+		return (-1);
+	}
+
+	l = as->length;
+	r = archive_string_append_from_wcs_in_codepage(
+	    as, aws.s, aws.length, sc);
+	if (r != 0 && errno != ENOMEM && l == as->length)
+		archive_string_append(as, s, length);
+	archive_wstring_free(&aws);
+	return (r);
+}
+
+/*
+ * Test whether MBS ==> WCS is okay.
+ */
+static int
+invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc)
+{
+	const char *p = (const char *)_p;
+	unsigned codepage;
+	DWORD mbflag = MB_ERR_INVALID_CHARS;
+
+	if (sc->flag & SCONV_FROM_CHARSET)
+		codepage = sc->to_cp;
+	else
+		codepage = sc->from_cp;
+
+	if (codepage == CP_C_LOCALE)
+		return (0);
+	if (codepage != CP_UTF8)
+		mbflag |= MB_PRECOMPOSED;
+
+	if (MultiByteToWideChar(codepage, mbflag, p, n, NULL, 0) == 0)
+		return (-1); /* Invalid */
+	return (0); /* Okay */
+}
+
+#else
+
+/*
+ * Test whether MBS ==> WCS is okay.
+ */
+static int
+invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc)
+{
+	const char *p = (const char *)_p;
+	size_t r;
+
+	(void)sc; /* UNUSED */
+#if HAVE_MBRTOWC
+	mbstate_t shift_state;
+
+	memset(&shift_state, 0, sizeof(shift_state));
+#else
+	/* Clear the shift state before starting. */
+	mbtowc(NULL, NULL, 0);
+#endif
+	while (n) {
+		wchar_t wc;
+
+#if HAVE_MBRTOWC
+		r = mbrtowc(&wc, p, n, &shift_state);
+#else
+		r = mbtowc(&wc, p, n);
+#endif
+		if (r == (size_t)-1 || r == (size_t)-2)
+			return (-1);/* Invalid. */
+		if (r == 0)
+			break;
+		p += r;
+		n -= r;
+	}
+	return (0); /* All Okey. */
+}
+
+#endif /* defined(_WIN32) && !defined(__CYGWIN__) */
+
+/*
+ * Basically returns -1 because we cannot make a conversion of charset
+ * without iconv but in some cases this would return 0.
+ * Returns 0 if all copied characters are ASCII.
+ * Returns 0 if both from-locale and to-locale are the same and those
+ * can be WCS with no error.
+ */
+static int
+best_effort_strncat_in_locale(struct archive_string *as, const void *_p,
+    size_t length, struct archive_string_conv *sc)
+{
+	size_t remaining;
+	char *outp;
+	const uint8_t *inp;
+	size_t avail;
+	int return_value = 0; /* success */
+
+	/*
+	 * If both from-locale and to-locale is the same, this makes a copy.
+	 * And then this checks all copied MBS can be WCS if so returns 0.
+	 */
+	if (sc->same) {
+		if (archive_string_append(as, _p, length) == NULL)
+			return (-1);/* No memory */
+		return (invalid_mbs(_p, length, sc));
+	}
+
+	/*
+	 * If a character is ASCII, this just copies it. If not, this
+	 * assigns '?' charater instead but in UTF-8 locale this assigns
+	 * byte sequence 0xEF 0xBD 0xBD, which are code point U+FFFD,
+	 * a Replacement Character in Unicode.
+	 */
+	if (archive_string_ensure(as, as->length + length + 1) == NULL)
+		return (-1);
+
+	remaining = length;
+	inp = (const uint8_t *)_p;
+	outp = as->s + as->length;
+	avail = as->buffer_length - as->length -1;
+	while (*inp && remaining > 0) {
+		if (*inp > 127 && (sc->flag & SCONV_TO_UTF8)) {
+			if (avail < UTF8_R_CHAR_SIZE) {
+				as->length = outp - as->s;
+				if (NULL == archive_string_ensure(as,
+				    as->buffer_length + remaining +
+				    UTF8_R_CHAR_SIZE))
+					return (-1);
+				outp = as->s + as->length;
+				avail = as->buffer_length - as->length -1;
+			}
+			/*
+		 	 * When coping a string in UTF-8, unknown character
+			 * should be U+FFFD (replacement character).
+			 */
+			UTF8_SET_R_CHAR(outp);
+			outp += UTF8_R_CHAR_SIZE;
+			avail -= UTF8_R_CHAR_SIZE;
+			inp++;
+			remaining--;
+			return_value = -1;
+		} else if (*inp > 127) {
+			*outp++ = '?';
+			inp++;
+			remaining--;
+			return_value = -1;
+		} else {
+			*outp++ = (char)*inp++;
+			remaining--;
+		}
+	}
+	as->length = outp - as->s;
+	as->s[as->length] = '\0';
+	return (return_value);
+}
+
+
+/*
+ * Unicode conversion functions.
+ *   - UTF-8 <===> UTF-8 in removing surrogate pairs.
+ *   - UTF-8 NFD ===> UTF-8 NFC in removing surrogate pairs.
+ *   - UTF-8 made by libarchive 2.x ===> UTF-8.
+ *   - UTF-16BE <===> UTF-8.
+ *
+ */
+
+/*
+ * Utility to convert a single UTF-8 sequence.
+ *
+ * Usually return used bytes, return used byte in negative value when
+ * a unicode character is replaced with U+FFFD.
+ * See also http://unicode.org/review/pr-121.html Public Review Issue #121
+ * Recommended Practice for Replacement Characters.
+ */
+static int
+_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+	static const char utf8_count[256] = {
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */
+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */
+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */
+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */
+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */
+		 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */
+		 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */
+		 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */
+		 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */
+	};
+	int ch, i;
+	int cnt;
+	uint32_t wc;
+
+	/* Sanity check. */
+	if (n == 0)
+		return (0);
+	/*
+	 * Decode 1-4 bytes depending on the value of the first byte.
+	 */
+	ch = (unsigned char)*s;
+	if (ch == 0)
+		return (0); /* Standard:  return 0 for end-of-string. */
+	cnt = utf8_count[ch];
+
+	/* Invalide sequence or there are not plenty bytes. */
+	if ((int)n < cnt) {
+		cnt = n;
+		for (i = 1; i < cnt; i++) {
+			if ((s[i] & 0xc0) != 0x80) {
+				cnt = i;
+				break;
+			}
+		}
+		goto invalid_sequence;
+	}
+
+	/* Make a Unicode code point from a single UTF-8 sequence. */
+	switch (cnt) {
+	case 1:	/* 1 byte sequence. */
+		*pwc = ch & 0x7f;
+		return (cnt);
+	case 2:	/* 2 bytes sequence. */
+		if ((s[1] & 0xc0) != 0x80) {
+			cnt = 1;
+			goto invalid_sequence;
+		}
+		*pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
+		return (cnt);
+	case 3:	/* 3 bytes sequence. */
+		if ((s[1] & 0xc0) != 0x80) {
+			cnt = 1;
+			goto invalid_sequence;
+		}
+		if ((s[2] & 0xc0) != 0x80) {
+			cnt = 2;
+			goto invalid_sequence;
+		}
+		wc = ((ch & 0x0f) << 12)
+		    | ((s[1] & 0x3f) << 6)
+		    | (s[2] & 0x3f);
+		if (wc < 0x800)
+			goto invalid_sequence;/* Overlong sequence. */
+		break;
+	case 4:	/* 4 bytes sequence. */
+		if ((s[1] & 0xc0) != 0x80) {
+			cnt = 1;
+			goto invalid_sequence;
+		}
+		if ((s[2] & 0xc0) != 0x80) {
+			cnt = 2;
+			goto invalid_sequence;
+		}
+		if ((s[3] & 0xc0) != 0x80) {
+			cnt = 3;
+			goto invalid_sequence;
+		}
+		wc = ((ch & 0x07) << 18)
+		    | ((s[1] & 0x3f) << 12)
+		    | ((s[2] & 0x3f) << 6)
+		    | (s[3] & 0x3f);
+		if (wc < 0x10000)
+			goto invalid_sequence;/* Overlong sequence. */
+		break;
+	default: /* Others are all invalid sequence. */
+		if (ch == 0xc0 || ch == 0xc1)
+			cnt = 2;
+		else if (ch >= 0xf5 && ch <= 0xf7)
+			cnt = 4;
+		else if (ch >= 0xf8 && ch <= 0xfb)
+			cnt = 5;
+		else if (ch == 0xfc || ch == 0xfd)
+			cnt = 6;
+		else
+			cnt = 1;
+		if ((int)n < cnt)
+			cnt = n;
+		for (i = 1; i < cnt; i++) {
+			if ((s[i] & 0xc0) != 0x80) {
+				cnt = i;
+				break;
+			}
+		}
+		goto invalid_sequence;
+	}
+
+	/* The code point larger than 0x10FFFF is not leagal
+	 * Unicode values. */
+	if (wc > UNICODE_MAX)
+		goto invalid_sequence;
+	/* Correctly gets a Unicode, returns used bytes. */
+	*pwc = wc;
+	return (cnt);
+invalid_sequence:
+	*pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */
+	return (cnt * -1);
+}
+
+static int
+utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+	int cnt;
+
+	cnt = _utf8_to_unicode(pwc, s, n);
+	/* Any of Surrogate pair is not leagal Unicode values. */
+	if (cnt == 3 && IS_SURROGATE_PAIR_LA(*pwc))
+		return (-3);
+	return (cnt);
+}
+
+static inline uint32_t
+combine_surrogate_pair(uint32_t uc, uint32_t uc2)
+{
+	uc -= 0xD800;
+	uc *= 0x400;
+	uc += uc2 - 0xDC00;
+	uc += 0x10000;
+	return (uc);
+}
+
+/*
+ * Convert a single UTF-8/CESU-8 sequence to a Unicode code point in
+ * removing surrogate pairs.
+ *
+ * CESU-8: The Compatibility Encoding Scheme for UTF-16.
+ *
+ * Usually return used bytes, return used byte in negative value when
+ * a unicode character is replaced with U+FFFD.
+ */
+static int
+cesu8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+	uint32_t wc, wc2;
+	int cnt;
+
+	cnt = _utf8_to_unicode(&wc, s, n);
+	if (cnt == 3 && IS_HIGH_SURROGATE_LA(wc)) {
+		if (n - 3 < 3) {
+			/* Invalid byte sequence. */
+			goto invalid_sequence;
+		}
+		cnt = _utf8_to_unicode(&wc2, s+3, n-3);
+		if (cnt != 3 || !IS_LOW_SURROGATE_LA(wc2)) {
+			/* Invalid byte sequence. */
+			goto invalid_sequence;
+		}
+		wc = combine_surrogate_pair(wc, wc2);
+		cnt = 6;
+	} else if (cnt == 3 && IS_LOW_SURROGATE_LA(wc)) {
+		/* Invalid byte sequence. */
+		goto invalid_sequence;
+	}
+	*pwc = wc;
+	return (cnt);
+invalid_sequence:
+	*pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */
+	if (cnt > 0)
+		cnt *= -1;
+	return (cnt);
+}
+
+/*
+ * Convert a Unicode code point to a single UTF-8 sequence.
+ *
+ * NOTE:This function does not check if the Unicode is leagal or not.
+ * Please you definitely check it before calling this.
+ */
+static size_t
+unicode_to_utf8(char *p, size_t remaining, uint32_t uc)
+{
+	char *_p = p;
+
+	/* Translate code point to UTF8 */
+	if (uc <= 0x7f) {
+		if (remaining == 0)
+			return (0);
+		*p++ = (char)uc;
+	} else if (uc <= 0x7ff) {
+		if (remaining < 2)
+			return (0);
+		*p++ = 0xc0 | ((uc >> 6) & 0x1f);
+		*p++ = 0x80 | (uc & 0x3f);
+	} else if (uc <= 0xffff) {
+		if (remaining < 3)
+			return (0);
+		*p++ = 0xe0 | ((uc >> 12) & 0x0f);
+		*p++ = 0x80 | ((uc >> 6) & 0x3f);
+		*p++ = 0x80 | (uc & 0x3f);
+	} else if (uc <= UNICODE_MAX) {
+		if (remaining < 4)
+			return (0);
+		*p++ = 0xf0 | ((uc >> 18) & 0x07);
+		*p++ = 0x80 | ((uc >> 12) & 0x3f);
+		*p++ = 0x80 | ((uc >> 6) & 0x3f);
+		*p++ = 0x80 | (uc & 0x3f);
+	} else {
+		/*
+		 * Undescribed code point should be U+FFFD
+		 * (replacement character).
+		 */
+		if (remaining < UTF8_R_CHAR_SIZE)
+			return (0);
+		UTF8_SET_R_CHAR(p);
+		p += UTF8_R_CHAR_SIZE;
+	}
+	return (p - _p);
+}
+
+static int
+utf16be_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+	return (utf16_to_unicode(pwc, s, n, 1));
+}
+
+static int
+utf16le_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+	return (utf16_to_unicode(pwc, s, n, 0));
+}
+
+static int
+utf16_to_unicode(uint32_t *pwc, const char *s, size_t n, int be)
+{
+	const char *utf16 = s;
+	unsigned uc;
+
+	if (n == 0)
+		return (0);
+	if (n == 1) {
+		/* set the Replacement Character instead. */
+		*pwc = UNICODE_R_CHAR;
+		return (-1);
+	}
+
+	if (be)
+		uc = archive_be16dec(utf16);
+	else
+		uc = archive_le16dec(utf16);
+	utf16 += 2;
+		
+	/* If this is a surrogate pair, assemble the full code point.*/
+	if (IS_HIGH_SURROGATE_LA(uc)) {
+		unsigned uc2;
+
+		if (n >= 4) {
+			if (be)
+				uc2 = archive_be16dec(utf16);
+			else
+				uc2 = archive_le16dec(utf16);
+		} else
+			uc2 = 0;
+		if (IS_LOW_SURROGATE_LA(uc2)) {
+			uc = combine_surrogate_pair(uc, uc2);
+			utf16 += 2;
+		} else {
+	 		/* Undescribed code point should be U+FFFD
+		 	* (replacement character). */
+			*pwc = UNICODE_R_CHAR;
+			return (-2);
+		}
+	}
+
+	/*
+	 * Surrogate pair values(0xd800 through 0xdfff) are only
+	 * used by UTF-16, so, after above culculation, the code
+	 * must not be surrogate values, and Unicode has no codes
+	 * larger than 0x10ffff. Thus, those are not leagal Unicode
+	 * values.
+	 */
+	if (IS_SURROGATE_PAIR_LA(uc) || uc > UNICODE_MAX) {
+	 	/* Undescribed code point should be U+FFFD
+	 	* (replacement character). */
+		*pwc = UNICODE_R_CHAR;
+		return (((int)(utf16 - s)) * -1);
+	}
+	*pwc = uc;
+	return ((int)(utf16 - s));
+}
+
+static size_t
+unicode_to_utf16be(char *p, size_t remaining, uint32_t uc)
+{
+	char *utf16 = p;
+
+	if (uc > 0xffff) {
+		/* We have a code point that won't fit into a
+		 * wchar_t; convert it to a surrogate pair. */
+		if (remaining < 4)
+			return (0);
+		uc -= 0x10000;
+		archive_be16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800);
+		archive_be16enc(utf16+2, (uc & 0x3ff) + 0xDC00);
+		return (4);
+	} else {
+		if (remaining < 2)
+			return (0);
+		archive_be16enc(utf16, uc);
+		return (2);
+	}
+}
+
+static size_t
+unicode_to_utf16le(char *p, size_t remaining, uint32_t uc)
+{
+	char *utf16 = p;
+
+	if (uc > 0xffff) {
+		/* We have a code point that won't fit into a
+		 * wchar_t; convert it to a surrogate pair. */
+		if (remaining < 4)
+			return (0);
+		uc -= 0x10000;
+		archive_le16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800);
+		archive_le16enc(utf16+2, (uc & 0x3ff) + 0xDC00);
+		return (4);
+	} else {
+		if (remaining < 2)
+			return (0);
+		archive_le16enc(utf16, uc);
+		return (2);
+	}
+}
+
+/*
+ * Copy UTF-8 string in checking surrogate pair.
+ * If any surrogate pair are found, it would be canonicalized.
+ */
+static int
+strncat_from_utf8_to_utf8(struct archive_string *as, const void *_p, size_t len,
+    struct archive_string_conv *sc)
+{
+	const char *s;
+	char *p, *endp;
+	int n, ret = 0;
+
+	(void)sc; /* UNUSED */
+
+	if (archive_string_ensure(as, as->length + len + 1) == NULL)
+		return (-1);
+
+	s = (const char *)_p;
+	p = as->s + as->length;
+	endp = as->s + as->buffer_length -1;
+	do {
+		uint32_t uc;
+		const char *ss = s;
+		size_t w;
+
+		/*
+		 * Forward byte sequence until a conversion of that is needed.
+		 */
+		while ((n = utf8_to_unicode(&uc, s, len)) > 0) {
+			s += n;
+			len -= n;
+		}
+		if (ss < s) {
+			if (p + (s - ss) > endp) {
+				as->length = p - as->s;
+				if (archive_string_ensure(as,
+				    as->buffer_length + len + 1) == NULL)
+					return (-1);
+				p = as->s + as->length;
+				endp = as->s + as->buffer_length -1;
+			}
+
+			memcpy(p, ss, s - ss);
+			p += s - ss;
+		}
+
+		/*
+		 * If n is negative, current byte sequence needs a replacement.
+		 */
+		if (n < 0) {
+			if (n == -3 && IS_SURROGATE_PAIR_LA(uc)) {
+				/* Current byte sequence may be CESU-8. */
+				n = cesu8_to_unicode(&uc, s, len);
+			}
+			if (n < 0) {
+				ret = -1;
+				n *= -1;/* Use a replaced unicode character. */
+			}
+
+			/* Rebuild UTF-8 byte sequence. */
+			while ((w = unicode_to_utf8(p, endp - p, uc)) == 0) {
+				as->length = p - as->s;
+				if (archive_string_ensure(as,
+				    as->buffer_length + len + 1) == NULL)
+					return (-1);
+				p = as->s + as->length;
+				endp = as->s + as->buffer_length -1;
+			}
+			p += w;
+			s += n;
+			len -= n;
+		}
+	} while (n > 0);
+	as->length = p - as->s;
+	as->s[as->length] = '\0';
+	return (ret);
+}
+
+static int
+archive_string_append_unicode(struct archive_string *as, const void *_p,
+    size_t len, struct archive_string_conv *sc)
+{
+	const char *s;
+	char *p, *endp;
+	uint32_t uc;
+	size_t w;
+	int n, ret = 0, ts, tm;
+	int (*parse)(uint32_t *, const char *, size_t);
+	size_t (*unparse)(char *, size_t, uint32_t);
+
+	if (sc->flag & SCONV_TO_UTF16BE) {
+		unparse = unicode_to_utf16be;
+		ts = 2;
+	} else if (sc->flag & SCONV_TO_UTF16LE) {
+		unparse = unicode_to_utf16le;
+		ts = 2;
+	} else if (sc->flag & SCONV_TO_UTF8) {
+		unparse = unicode_to_utf8;
+		ts = 1;
+	} else {
+		/*
+		 * This case is going to be converted to another
+		 * character-set through iconv.
+		 */
+		if (sc->flag & SCONV_FROM_UTF16BE) {
+			unparse = unicode_to_utf16be;
+			ts = 2;
+		} else if (sc->flag & SCONV_FROM_UTF16LE) {
+			unparse = unicode_to_utf16le;
+			ts = 2;
+		} else {
+			unparse = unicode_to_utf8;
+			ts = 1;
+		}
+	}
+
+	if (sc->flag & SCONV_FROM_UTF16BE) {
+		parse = utf16be_to_unicode;
+		tm = 1;
+	} else if (sc->flag & SCONV_FROM_UTF16LE) {
+		parse = utf16le_to_unicode;
+		tm = 1;
+	} else {
+		parse = cesu8_to_unicode;
+		tm = ts;
+	}
+
+	if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
+		return (-1);
+
+	s = (const char *)_p;
+	p = as->s + as->length;
+	endp = as->s + as->buffer_length - ts;
+	while ((n = parse(&uc, s, len)) != 0) {
+		if (n < 0) {
+			/* Use a replaced unicode character. */
+			n *= -1;
+			ret = -1;
+		}
+		s += n;
+		len -= n;
+		while ((w = unparse(p, endp - p, uc)) == 0) {
+			/* There is not enough output buffer so
+			 * we have to expand it. */
+			as->length = p - as->s;
+			if (archive_string_ensure(as,
+			    as->buffer_length + len * tm + ts) == NULL)
+				return (-1);
+			p = as->s + as->length;
+			endp = as->s + as->buffer_length - ts;
+		}
+		p += w;
+	}
+	as->length = p - as->s;
+	as->s[as->length] = '\0';
+	if (ts == 2)
+		as->s[as->length+1] = '\0';
+	return (ret);
+}
+
+/*
+ * Following Constants for Hangul compositions this information comes from
+ * Unicode Standard Annex #15  http://unicode.org/reports/tr15/
+ */
+#define HC_SBASE	0xAC00
+#define HC_LBASE	0x1100
+#define HC_VBASE	0x1161
+#define HC_TBASE	0x11A7
+#define HC_LCOUNT	19
+#define HC_VCOUNT	21
+#define HC_TCOUNT	28
+#define HC_NCOUNT	(HC_VCOUNT * HC_TCOUNT)
+#define HC_SCOUNT	(HC_LCOUNT * HC_NCOUNT)
+
+static uint32_t
+get_nfc(uint32_t uc, uint32_t uc2)
+{
+	int t, b;
+
+	t = 0;
+	b = sizeof(u_composition_table)/sizeof(u_composition_table[0]) -1;
+	while (b >= t) {
+		int m = (t + b) / 2;
+		if (u_composition_table[m].cp1 < uc)
+			t = m + 1;
+		else if (u_composition_table[m].cp1 > uc)
+			b = m - 1;
+		else if (u_composition_table[m].cp2 < uc2)
+			t = m + 1;
+		else if (u_composition_table[m].cp2 > uc2)
+			b = m - 1;
+		else
+			return (u_composition_table[m].nfc);
+	}
+	return (0);
+}
+
+#define FDC_MAX 10	/* The maximum number of Following Decomposable
+			 * Characters. */
+
+/*
+ * Update first code point.
+ */
+#define UPDATE_UC(new_uc)	do {		\
+	uc = new_uc;				\
+	ucptr = NULL;				\
+} while (0)
+
+/*
+ * Replace first code point with second code point.
+ */
+#define REPLACE_UC_WITH_UC2() do {		\
+	uc = uc2;				\
+	ucptr = uc2ptr;				\
+	n = n2;					\
+} while (0)
+
+#define EXPAND_BUFFER() do {			\
+	as->length = p - as->s;			\
+	if (archive_string_ensure(as,		\
+	    as->buffer_length + len * tm + ts) == NULL)\
+		return (-1);			\
+	p = as->s + as->length;			\
+	endp = as->s + as->buffer_length - ts;	\
+} while (0)
+
+#define UNPARSE(p, endp, uc)	do {		\
+	while ((w = unparse(p, (endp) - (p), uc)) == 0) {\
+		EXPAND_BUFFER();		\
+	}					\
+	p += w;					\
+} while (0)
+
+/*
+ * Write first code point.
+ * If the code point has not be changed from its original code,
+ * this just copies it from its original buffer pointer.
+ * If not, this converts it to UTF-8 byte sequence and copies it.
+ */
+#define WRITE_UC()	do {			\
+	if (ucptr) {				\
+		if (p + n > endp)		\
+			EXPAND_BUFFER();	\
+		switch (n) {			\
+		case 4:				\
+			*p++ = *ucptr++;	\
+			/* FALL THROUGH */	\
+		case 3:				\
+			*p++ = *ucptr++;	\
+			/* FALL THROUGH */	\
+		case 2:				\
+			*p++ = *ucptr++;	\
+			/* FALL THROUGH */	\
+		case 1:				\
+			*p++ = *ucptr;		\
+			break;			\
+		}				\
+		ucptr = NULL;			\
+	} else {				\
+		UNPARSE(p, endp, uc);		\
+	}					\
+} while (0)
+
+/*
+ * Collect following decomposable code points.
+ */
+#define COLLECT_CPS(start)	do {		\
+	int _i;					\
+	for (_i = start; _i < FDC_MAX ; _i++) {	\
+		nx = parse(&ucx[_i], s, len);	\
+		if (nx <= 0)			\
+			break;			\
+		cx = CCC(ucx[_i]);		\
+		if (cl >= cx && cl != 228 && cx != 228)\
+			break;			\
+		s += nx;			\
+		len -= nx;			\
+		cl = cx;			\
+		ccx[_i] = cx;			\
+	}					\
+	if (_i >= FDC_MAX) {			\
+		ret = -1;			\
+		ucx_size = FDC_MAX;		\
+	} else					\
+		ucx_size = _i;			\
+} while (0)
+
+/*
+ * Normalize UTF-8/UTF-16BE characters to Form C and copy the result.
+ *
+ * TODO: Convert composition exclusions,which are never converted
+ * from NFC,NFD,NFKC and NFKD, to Form C.
+ */
+static int
+archive_string_normalize_C(struct archive_string *as, const void *_p,
+    size_t len, struct archive_string_conv *sc)
+{
+	const char *s = (const char *)_p;
+	char *p, *endp;
+	uint32_t uc, uc2;
+	size_t w;
+	int always_replace, n, n2, ret = 0, spair, ts, tm;
+	int (*parse)(uint32_t *, const char *, size_t);
+	size_t (*unparse)(char *, size_t, uint32_t);
+
+	always_replace = 1;
+	ts = 1;/* text size. */
+	if (sc->flag & SCONV_TO_UTF16BE) {
+		unparse = unicode_to_utf16be;
+		ts = 2;
+		if (sc->flag & SCONV_FROM_UTF16BE)
+			always_replace = 0;
+	} else if (sc->flag & SCONV_TO_UTF16LE) {
+		unparse = unicode_to_utf16le;
+		ts = 2;
+		if (sc->flag & SCONV_FROM_UTF16LE)
+			always_replace = 0;
+	} else if (sc->flag & SCONV_TO_UTF8) {
+		unparse = unicode_to_utf8;
+		if (sc->flag & SCONV_FROM_UTF8)
+			always_replace = 0;
+	} else {
+		/*
+		 * This case is going to be converted to another
+		 * character-set through iconv.
+		 */
+		always_replace = 0;
+		if (sc->flag & SCONV_FROM_UTF16BE) {
+			unparse = unicode_to_utf16be;
+			ts = 2;
+		} else if (sc->flag & SCONV_FROM_UTF16LE) {
+			unparse = unicode_to_utf16le;
+			ts = 2;
+		} else {
+			unparse = unicode_to_utf8;
+		}
+	}
+
+	if (sc->flag & SCONV_FROM_UTF16BE) {
+		parse = utf16be_to_unicode;
+		tm = 1;
+		spair = 4;/* surrogate pair size in UTF-16. */
+	} else if (sc->flag & SCONV_FROM_UTF16LE) {
+		parse = utf16le_to_unicode;
+		tm = 1;
+		spair = 4;/* surrogate pair size in UTF-16. */
+	} else {
+		parse = cesu8_to_unicode;
+		tm = ts;
+		spair = 6;/* surrogate pair size in UTF-8. */
+	}
+
+	if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
+		return (-1);
+
+	p = as->s + as->length;
+	endp = as->s + as->buffer_length - ts;
+	while ((n = parse(&uc, s, len)) != 0) {
+		const char *ucptr, *uc2ptr;
+
+		if (n < 0) {
+			/* Use a replaced unicode character. */
+			UNPARSE(p, endp, uc);
+			s += n*-1;
+			len -= n*-1;
+			ret = -1;
+			continue;
+		} else if (n == spair || always_replace)
+			/* uc is converted from a surrogate pair.
+			 * this should be treated as a changed code. */
+			ucptr = NULL;
+		else
+			ucptr = s;
+		s += n;
+		len -= n;
+
+		/* Read second code point. */
+		while ((n2 = parse(&uc2, s, len)) > 0) {
+			uint32_t ucx[FDC_MAX];
+			int ccx[FDC_MAX];
+			int cl, cx, i, nx, ucx_size;
+			int LIndex,SIndex;
+			uint32_t nfc;
+
+			if (n2 == spair || always_replace)
+				/* uc2 is converted from a surrogate pair.
+			 	 * this should be treated as a changed code. */
+				uc2ptr = NULL;
+			else
+				uc2ptr = s;
+			s += n2;
+			len -= n2;
+
+			/*
+			 * If current second code point is out of decomposable
+			 * code points, finding compositions is unneeded.
+			 */
+			if (!IS_DECOMPOSABLE_BLOCK(uc2)) {
+				WRITE_UC();
+				REPLACE_UC_WITH_UC2();
+				continue;
+			}
+
+			/*
+			 * Try to combine current code points.
+			 */
+			/*
+			 * We have to combine Hangul characters according to
+			 * http://uniicode.org/reports/tr15/#Hangul
+			 */
+			if (0 <= (LIndex = uc - HC_LBASE) &&
+			    LIndex < HC_LCOUNT) {
+				/*
+				 * Hangul Composition.
+				 * 1. Two current code points are L and V.
+				 */
+				int VIndex = uc2 - HC_VBASE;
+				if (0 <= VIndex && VIndex < HC_VCOUNT) {
+					/* Make syllable of form LV. */
+					UPDATE_UC(HC_SBASE +
+					    (LIndex * HC_VCOUNT + VIndex) *
+					     HC_TCOUNT);
+				} else {
+					WRITE_UC();
+					REPLACE_UC_WITH_UC2();
+				}
+				continue;
+			} else if (0 <= (SIndex = uc - HC_SBASE) &&
+			    SIndex < HC_SCOUNT && (SIndex % HC_TCOUNT) == 0) {
+				/*
+				 * Hangul Composition.
+				 * 2. Two current code points are LV and T.
+				 */
+				int TIndex = uc2 - HC_TBASE;
+				if (0 < TIndex && TIndex < HC_TCOUNT) {
+					/* Make syllable of form LVT. */
+					UPDATE_UC(uc + TIndex);
+				} else {
+					WRITE_UC();
+					REPLACE_UC_WITH_UC2();
+				}
+				continue;
+			} else if ((nfc = get_nfc(uc, uc2)) != 0) {
+				/* A composition to current code points
+				 * is found. */
+				UPDATE_UC(nfc);
+				continue;
+			} else if ((cl = CCC(uc2)) == 0) {
+				/* Clearly 'uc2' the second code point is not
+				 * a decomposable code. */
+				WRITE_UC();
+				REPLACE_UC_WITH_UC2();
+				continue;
+			}
+
+			/*
+			 * Collect following decomposable code points.
+			 */
+			cx = 0;
+			ucx[0] = uc2;
+			ccx[0] = cl;
+			COLLECT_CPS(1);
+
+			/*
+			 * Find a composed code in the collected code points.
+			 */
+			i = 1;
+			while (i < ucx_size) {
+				int j;
+
+				if ((nfc = get_nfc(uc, ucx[i])) == 0) {
+					i++;
+					continue;
+				}
+
+				/*
+				 * nfc is composed of uc and ucx[i].
+				 */
+				UPDATE_UC(nfc);
+
+				/*
+				 * Remove ucx[i] by shifting
+				 * following code points.
+				 */
+				for (j = i; j+1 < ucx_size; j++) {
+					ucx[j] = ucx[j+1];
+					ccx[j] = ccx[j+1];
+				}
+				ucx_size --;
+
+				/*
+				 * Collect following code points blocked
+				 * by ucx[i] the removed code point.
+				 */
+				if (ucx_size > 0 && i == ucx_size &&
+				    nx > 0 && cx == cl) {
+					cl =  ccx[ucx_size-1];
+					COLLECT_CPS(ucx_size);
+				}
+				/*
+				 * Restart finding a composed code with
+				 * the updated uc from the top of the
+				 * collected code points.
+				 */
+				i = 0;
+			}
+
+			/*
+			 * Apparently the current code points are not
+			 * decomposed characters or already composed.
+			 */
+			WRITE_UC();
+			for (i = 0; i < ucx_size; i++)
+				UNPARSE(p, endp, ucx[i]);
+
+			/*
+			 * Flush out remaining canonical combining characters.
+			 */
+			if (nx > 0 && cx == cl && len > 0) {
+				while ((nx = parse(&ucx[0], s, len))
+				    > 0) {
+					cx = CCC(ucx[0]);
+					if (cl > cx)
+						break;
+					s += nx;
+					len -= nx;
+					cl = cx;
+					UNPARSE(p, endp, ucx[0]);
+				}
+			}
+			break;
+		}
+		if (n2 < 0) {
+			WRITE_UC();
+			/* Use a replaced unicode character. */
+			UNPARSE(p, endp, uc2);
+			s += n2*-1;
+			len -= n2*-1;
+			ret = -1;
+			continue;
+		} else if (n2 == 0) {
+			WRITE_UC();
+			break;
+		}
+	}
+	as->length = p - as->s;
+	as->s[as->length] = '\0';
+	if (ts == 2)
+		as->s[as->length+1] = '\0';
+	return (ret);
+}
+
+#if defined(__APPLE__)
+
+/*
+ * Normalize UTF-8 characters to Form D and copy the result.
+ */
+static int
+archive_string_normalize_D(struct archive_string *as, const void *_p,
+    size_t len, struct archive_string_conv *sc)
+{
+	const UniChar *inp;
+	char *outp;
+	size_t newsize;
+	ByteCount inCount, outCount;
+	ByteCount inAvail, outAvail;
+	OSStatus err;
+	int ret, saved_flag;
+
+	/*
+	 * Convert the current string to UTF-16LE for normalization.
+	 * The character-set of the current string must be UTF-16BE or
+	 * UTF-8.
+	 */
+	archive_string_empty(&(sc->utf16nfc));
+	saved_flag = sc->flag;/* save a flag. */
+	sc->flag &= ~(SCONV_TO_UTF16BE | SCONV_TO_UTF8);
+	sc->flag |= SCONV_TO_UTF16LE;
+	ret = archive_string_append_unicode(&(sc->utf16nfc), _p, len, sc);
+	sc->flag = saved_flag;/* restore the saved flag */
+	if (archive_strlen(&(sc->utf16nfc)) == 0) {
+		if (archive_string_ensure(as, as->length + 1) == NULL)
+			return (-1);
+		return (ret);
+	}
+
+	/*
+	 * Normalize an NFC string to be an NFD(HFS Plus version).
+	 */
+	newsize = sc->utf16nfc.length + 2;
+	if (archive_string_ensure(&(sc->utf16nfd), newsize) == NULL)
+		return (-1);
+
+	inp = (UniChar *)sc->utf16nfc.s;
+	inAvail = archive_strlen(&(sc->utf16nfc));
+	sc->utf16nfd.length = 0;
+	outp = sc->utf16nfd.s;
+	outAvail = sc->utf16nfd.buffer_length -2;
+
+	do {
+		/* Reinitialize all state information. */
+		if (ResetUnicodeToTextInfo(sc->uniInfo) != noErr)
+			goto return_no_changed_data;
+
+		inCount = outCount = 0;
+		err = ConvertFromUnicodeToText(sc->uniInfo,
+		    inAvail, inp,
+		    kUnicodeDefaultDirectionMask, 0, NULL, NULL, NULL,
+		    outAvail, &inCount, &outCount, outp);
+
+		if (err == noErr) {
+			sc->utf16nfd.length = outCount;
+			sc->utf16nfd.s[sc->utf16nfd.length] = 0;
+			sc->utf16nfd.s[sc->utf16nfd.length+1] = 0;
+		} else if (err == kTECOutputBufferFullStatus) {
+			newsize = inAvail - inCount;
+			if (newsize > inAvail)
+				newsize = inAvail;
+			newsize += sc->utf16nfd.buffer_length + 2;
+			if (archive_string_ensure(&(sc->utf16nfd), newsize)
+			    == NULL)
+				return (-1);
+			outp = sc->utf16nfd.s;
+			outAvail = sc->utf16nfd.buffer_length -2;
+		} else
+			goto return_no_changed_data;
+	} while (err == kTECOutputBufferFullStatus);
+
+	/*
+	 * If there is a next-step conversion, we should convert
+	 * a UTF-16LE(NFD) string back to the original Unicode type.
+	 */
+	saved_flag = sc->flag;/* save a flag. */
+	if (!(sc->flag &
+	    (SCONV_TO_UTF16BE | SCONV_TO_UTF16LE | SCONV_TO_UTF8))) {
+		/*
+		 * This case is going to be converted to another
+		 * character-set through iconv.
+		 */
+		if (sc->flag & SCONV_FROM_UTF16BE)
+			sc->flag |= SCONV_TO_UTF16BE;
+		else if (sc->flag & SCONV_FROM_UTF16LE)
+			sc->flag |= SCONV_TO_UTF16LE;
+		else
+			sc->flag |= SCONV_TO_UTF8;
+	}
+	sc->flag &= ~(SCONV_FROM_UTF16BE | SCONV_FROM_UTF8);
+	sc->flag |= SCONV_FROM_UTF16LE;
+	if (archive_string_append_unicode(as, sc->utf16nfd.s,
+	    sc->utf16nfd.length, sc) != 0)
+		ret = -1;
+	sc->flag = saved_flag;/* restore the saved flag */
+	return (ret);
+
+return_no_changed_data:
+	/*
+	 * Something conversion error happened, so we return a no normalized
+	 * string with an error.
+	 */
+	(void)archive_string_append_unicode(as, _p, len, sc);
+	return (-1);
+}
+
+#endif /* __APPLE__ */
+
+/*
+ * libarchive 2.x made incorrect UTF-8 strings in the wrong assumption
+ * that WCS is Unicode. It is true for several platforms but some are false.
+ * And then people who did not use UTF-8 locale on the non Unicode WCS
+ * platform and made a tar file with libarchive(mostly bsdtar) 2.x. Those
+ * now cannot get right filename from libarchive 3.x and later since we
+ * fixed the wrong assumption and it is incompatible to older its versions.
+ * So we provide special option, "compat-2x.x", for resolving it.
+ * That option enable the string conversion of libarchive 2.x.
+ *
+ * Translates the wrong UTF-8 string made by libarchive 2.x into current
+ * locale character set and appends to the archive_string.
+ * Note: returns -1 if conversion fails.
+ */
+static int
+strncat_from_utf8_libarchive2(struct archive_string *as,
+    const void *_p, size_t len, struct archive_string_conv *sc)
+{
+	const char *s;
+	int n;
+	char *p;
+	char *end;
+	uint32_t unicode;
+#if HAVE_WCRTOMB
+	mbstate_t shift_state;
+
+	memset(&shift_state, 0, sizeof(shift_state));
+#else
+	/* Clear the shift state before starting. */
+	wctomb(NULL, L'\0');
+#endif
+	(void)sc; /* UNUSED */
+	/*
+	 * Allocate buffer for MBS.
+	 * We need this allocation here since it is possible that
+	 * as->s is still NULL.
+	 */
+	if (archive_string_ensure(as, as->length + len + 1) == NULL)
+		return (-1);
+
+	s = (const char *)_p;
+	p = as->s + as->length;
+	end = as->s + as->buffer_length - MB_CUR_MAX -1;
+	while ((n = _utf8_to_unicode(&unicode, s, len)) != 0) {
+		wchar_t wc;
+
+		if (p >= end) {
+			as->length = p - as->s;
+			/* Re-allocate buffer for MBS. */
+			if (archive_string_ensure(as,
+			    as->length + len * 2 + 1) == NULL)
+				return (-1);
+			p = as->s + as->length;
+			end = as->s + as->buffer_length - MB_CUR_MAX -1;
+		}
+
+		/*
+		 * As libarchie 2.x, translates the UTF-8 characters into
+		 * wide-characters in the assumption that WCS is Unicode.
+		 */
+		if (n < 0) {
+			n *= -1;
+			wc = L'?';
+		} else
+			wc = (wchar_t)unicode;
+
+		s += n;
+		len -= n;
+		/*
+		 * Translates the wide-character into the current locale MBS.
+		 */
+#if HAVE_WCRTOMB
+		n = wcrtomb(p, wc, &shift_state);
+#else
+		n = wctomb(p, wc);
+#endif
 		if (n == -1)
-			return (NULL);
+			return (-1);
 		p += n;
 	}
-	*p = '\0';
-	archive_strcat(as, buff);
-	return (as);
+	as->length = p - as->s;
+	as->s[as->length] = '\0';
+	return (0);
+}
+
+
+/*
+ * Conversion functions between current locale dependent MBS and UTF-16BE.
+ *   strncat_from_utf16be() : UTF-16BE --> MBS
+ *   strncat_to_utf16be()   : MBS --> UTF16BE
+ */
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Convert a UTF-16BE/LE string to current locale and copy the result.
+ * Return -1 if conversion failes.
+ */
+static int
+win_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes,
+    struct archive_string_conv *sc, int be)
+{
+	struct archive_string tmp;
+	const char *u16;
+	int ll;
+	BOOL defchar;
+	char *mbs;
+	size_t mbs_size, b;
+	int ret = 0;
+
+	bytes &= ~1;
+	if (archive_string_ensure(as, as->length + bytes +1) == NULL)
+		return (-1);
+
+	mbs = as->s + as->length;
+	mbs_size = as->buffer_length - as->length -1;
+
+	if (sc->to_cp == CP_C_LOCALE) {
+		/*
+		 * "C" locale special process.
+		 */
+		u16 = _p;
+		ll = 0;
+		for (b = 0; b < bytes; b += 2) {
+			uint16_t val;
+			if (be)
+				val = archive_be16dec(u16+b);
+			else
+				val = archive_le16dec(u16+b);
+			if (val > 255) {
+				*mbs++ = '?';
+				ret = -1;
+			} else
+				*mbs++ = (char)(val&0xff);
+			ll++;
+		}
+		as->length += ll;
+		as->s[as->length] = '\0';
+		return (ret);
+	}
+
+	archive_string_init(&tmp);
+	if (be) {
+		if (is_big_endian()) {
+			u16 = _p;
+		} else {
+			if (archive_string_ensure(&tmp, bytes+2) == NULL)
+				return (-1);
+			memcpy(tmp.s, _p, bytes);
+			for (b = 0; b < bytes; b += 2) {
+				uint16_t val = archive_be16dec(tmp.s+b);
+				archive_le16enc(tmp.s+b, val);
+			}
+			u16 = tmp.s;
+		}
+	} else {
+		if (!is_big_endian()) {
+			u16 = _p;
+		} else {
+			if (archive_string_ensure(&tmp, bytes+2) == NULL)
+				return (-1);
+			memcpy(tmp.s, _p, bytes);
+			for (b = 0; b < bytes; b += 2) {
+				uint16_t val = archive_le16dec(tmp.s+b);
+				archive_be16enc(tmp.s+b, val);
+			}
+			u16 = tmp.s;
+		}
+	}
+
+	do {
+		defchar = 0;
+		ll = WideCharToMultiByte(sc->to_cp, 0,
+		    (LPCWSTR)u16, bytes>>1, mbs, mbs_size,
+			NULL, &defchar);
+		if (ll == 0 &&
+		    GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+			/* Need more buffer for MBS. */
+			ll = WideCharToMultiByte(sc->to_cp, 0,
+			    (LPCWSTR)u16, bytes, NULL, 0, NULL, NULL);
+			if (archive_string_ensure(as, ll +1) == NULL)
+				return (-1);
+			mbs = as->s + as->length;
+			mbs_size = as->buffer_length - as->length -1;
+			continue;
+		}
+	} while (0);
+	archive_string_free(&tmp);
+	as->length += ll;
+	as->s[as->length] = '\0';
+	if (ll == 0 || defchar)
+		ret = -1;
+	return (ret);
+}
+
+static int
+win_strncat_from_utf16be(struct archive_string *as, const void *_p, size_t bytes,
+    struct archive_string_conv *sc)
+{
+	return (win_strncat_from_utf16(as, _p, bytes, sc, 1));
+}
+
+static int
+win_strncat_from_utf16le(struct archive_string *as, const void *_p, size_t bytes,
+    struct archive_string_conv *sc)
+{
+	return (win_strncat_from_utf16(as, _p, bytes, sc, 0));
+}
+
+static int
+is_big_endian(void)
+{
+	uint16_t d = 1;
+
+	return (archive_be16dec(&d) == 1);
+}
+
+/*
+ * Convert a current locale string to UTF-16BE/LE and copy the result.
+ * Return -1 if conversion failes.
+ */
+static int
+win_strncat_to_utf16(struct archive_string *as16, const void *_p, size_t length,
+    struct archive_string_conv *sc, int bigendian)
+{
+	const char *s = (const char *)_p;
+	char *u16;
+	size_t count, avail;
+
+	if (archive_string_ensure(as16,
+	    as16->length + (length + 1) * 2) == NULL)
+		return (-1);
+
+	u16 = as16->s + as16->length;
+	avail = as16->buffer_length - 2;
+	if (sc->from_cp == CP_C_LOCALE) {
+		/*
+		 * "C" locale special process.
+		 */
+		count = 0;
+		while (count < length && *s) {
+			if (bigendian)
+				archive_be16enc(u16, *s);
+			else
+				archive_le16enc(u16, *s);
+			u16 += 2;
+			s++;
+			count++;
+		}
+		as16->length += count << 1;
+		as16->s[as16->length] = 0;
+		as16->s[as16->length+1] = 0;
+		return (0);
+	}
+	do {
+		count = MultiByteToWideChar(sc->from_cp,
+		    MB_PRECOMPOSED, s, length, (LPWSTR)u16, (int)avail>>1);
+		if (count == 0 &&
+		    GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+			/* Need more buffer for UTF-16 string */
+			count = MultiByteToWideChar(sc->from_cp,
+			    MB_PRECOMPOSED, s, length, NULL, 0);
+			if (archive_string_ensure(as16, (count +1) * 2)
+			    == NULL)
+				return (-1);
+			u16 = as16->s + as16->length;
+			avail = as16->buffer_length - 2;
+			continue;
+		}
+	} while (0);
+	as16->length += count * 2;
+	as16->s[as16->length] = 0;
+	as16->s[as16->length+1] = 0;
+	if (count == 0)
+		return (-1);
+
+	if (is_big_endian()) {
+		if (!bigendian) {
+			while (count > 0) {
+				uint16_t v = archive_be16dec(u16);
+				archive_le16enc(u16, v);
+				u16 += 2;
+				count--;
+			}
+		}
+	} else {
+		if (bigendian) {
+			while (count > 0) {
+				uint16_t v = archive_le16dec(u16);
+				archive_be16enc(u16, v);
+				u16 += 2;
+				count--;
+			}
+		}
+	}
+	return (0);
+}
+
+static int
+win_strncat_to_utf16be(struct archive_string *as16, const void *_p, size_t length,
+    struct archive_string_conv *sc)
+{
+	return (win_strncat_to_utf16(as16, _p, length, sc, 1));
+}
+
+static int
+win_strncat_to_utf16le(struct archive_string *as16, const void *_p, size_t length,
+    struct archive_string_conv *sc)
+{
+	return (win_strncat_to_utf16(as16, _p, length, sc, 0));
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
+
+/*
+ * Do the best effort for conversions.
+ * We cannot handle UTF-16BE character-set without such iconv,
+ * but there is a chance if a string consists just ASCII code or
+ * a current locale is UTF-8.
+ */
+
+/*
+ * Convert a UTF-16BE string to current locale and copy the result.
+ * Return -1 if conversion failes.
+ */
+static int
+best_effort_strncat_from_utf16(struct archive_string *as, const void *_p,
+    size_t bytes, struct archive_string_conv *sc, int be)
+{
+	const char *utf16 = (const char *)_p;
+	char *mbs;
+	uint32_t uc;
+	int n, ret;
+
+	(void)sc; /* UNUSED */
+	/*
+	 * Other case, we should do the best effort.
+	 * If all character are ASCII(<0x7f), we can convert it.
+	 * if not , we set a alternative character and return -1.
+	 */
+	ret = 0;
+	if (archive_string_ensure(as, as->length + bytes +1) == NULL)
+		return (-1);
+	mbs = as->s + as->length;
+
+	while ((n = utf16_to_unicode(&uc, utf16, bytes, be)) != 0) {
+		if (n < 0) {
+			n *= -1;
+			ret =  -1;
+		}
+		bytes -= n;
+		utf16 += n;
+
+		if (uc > 127) {
+			/* We cannot handle it. */
+			*mbs++ = '?';
+			ret =  -1;
+		} else
+			*mbs++ = (char)uc;
+	}
+	as->length = mbs - as->s;
+	as->s[as->length] = '\0';
+	return (ret);
+}
+
+static int
+best_effort_strncat_from_utf16be(struct archive_string *as, const void *_p,
+    size_t bytes, struct archive_string_conv *sc)
+{
+	return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 1));
+}
+
+static int
+best_effort_strncat_from_utf16le(struct archive_string *as, const void *_p,
+    size_t bytes, struct archive_string_conv *sc)
+{
+	return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 0));
+}
+
+/*
+ * Convert a current locale string to UTF-16BE/LE and copy the result.
+ * Return -1 if conversion failes.
+ */
+static int
+best_effort_strncat_to_utf16(struct archive_string *as16, const void *_p,
+    size_t length, struct archive_string_conv *sc, int bigendian)
+{
+	const char *s = (const char *)_p;
+	char *utf16;
+	size_t remaining;
+	int ret;
+
+	(void)sc; /* UNUSED */
+	/*
+	 * Other case, we should do the best effort.
+	 * If all character are ASCII(<0x7f), we can convert it.
+	 * if not , we set a alternative character and return -1.
+	 */
+	ret = 0;
+	remaining = length;
+
+	if (archive_string_ensure(as16,
+	    as16->length + (length + 1) * 2) == NULL)
+		return (-1);
+
+	utf16 = as16->s + as16->length;
+	while (remaining--) {
+		unsigned c = *s++;
+		if (c > 127) {
+			/* We cannot handle it. */
+			c = UNICODE_R_CHAR;
+			ret = -1;
+		}
+		if (bigendian)
+			archive_be16enc(utf16, c);
+		else
+			archive_le16enc(utf16, c);
+		utf16 += 2;
+	}
+	as16->length = utf16 - as16->s;
+	as16->s[as16->length] = 0;
+	as16->s[as16->length+1] = 0;
+	return (ret);
+}
+
+static int
+best_effort_strncat_to_utf16be(struct archive_string *as16, const void *_p,
+    size_t length, struct archive_string_conv *sc)
+{
+	return (best_effort_strncat_to_utf16(as16, _p, length, sc, 1));
+}
+
+static int
+best_effort_strncat_to_utf16le(struct archive_string *as16, const void *_p,
+    size_t length, struct archive_string_conv *sc)
+{
+	return (best_effort_strncat_to_utf16(as16, _p, length, sc, 0));
+}
+
+
+/*
+ * Multistring operations.
+ */
+
+void
+archive_mstring_clean(struct archive_mstring *aes)
+{
+	archive_wstring_free(&(aes->aes_wcs));
+	archive_string_free(&(aes->aes_mbs));
+	archive_string_free(&(aes->aes_utf8));
+	archive_string_free(&(aes->aes_mbs_in_locale));
+	aes->aes_set = 0;
+}
+
+void
+archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src)
+{
+	dest->aes_set = src->aes_set;
+	archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs));
+	archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8));
+	archive_wstring_copy(&(dest->aes_wcs), &(src->aes_wcs));
+}
+
+int
+archive_mstring_get_utf8(struct archive *a, struct archive_mstring *aes,
+  const char **p)
+{
+	struct archive_string_conv *sc;
+	int r;
+
+	/* If we already have a UTF8 form, return that immediately. */
+	if (aes->aes_set & AES_SET_UTF8) {
+		*p = aes->aes_utf8.s;
+		return (0);
+	}
+
+	*p = NULL;
+	if (aes->aes_set & AES_SET_MBS) {
+		sc = archive_string_conversion_to_charset(a, "UTF-8", 1);
+		if (sc == NULL)
+			return (-1);/* Couldn't allocate memory for sc. */
+		r = archive_strncpy_in_locale(&(aes->aes_mbs), aes->aes_mbs.s,
+		    aes->aes_mbs.length, sc);
+		if (a == NULL)
+			free_sconv_object(sc);
+		if (r == 0) {
+			aes->aes_set |= AES_SET_UTF8;
+			*p = aes->aes_utf8.s;
+			return (0);/* success. */
+		} else
+			return (-1);/* failure. */
+	}
+	return (0);/* success. */
+}
+
+int
+archive_mstring_get_mbs(struct archive *a, struct archive_mstring *aes,
+    const char **p)
+{
+	int r, ret = 0;
+
+	(void)a; /* UNUSED */
+	/* If we already have an MBS form, return that immediately. */
+	if (aes->aes_set & AES_SET_MBS) {
+		*p = aes->aes_mbs.s;
+		return (ret);
+	}
+
+	*p = NULL;
+	/* If there's a WCS form, try converting with the native locale. */
+	if (aes->aes_set & AES_SET_WCS) {
+		archive_string_empty(&(aes->aes_mbs));
+		r = archive_string_append_from_wcs(&(aes->aes_mbs),
+		    aes->aes_wcs.s, aes->aes_wcs.length);
+		*p = aes->aes_mbs.s;
+		if (r == 0) {
+			aes->aes_set |= AES_SET_MBS;
+			return (ret);
+		} else
+			ret = -1;
+	}
+
+	/*
+	 * Only a UTF-8 form cannot avail because its conversion already
+	 * failed at archive_mstring_update_utf8().
+	 */
+	return (ret);
+}
+
+int
+archive_mstring_get_wcs(struct archive *a, struct archive_mstring *aes,
+    const wchar_t **wp)
+{
+	int r, ret = 0;
+
+	(void)a;/* UNUSED */
+	/* Return WCS form if we already have it. */
+	if (aes->aes_set & AES_SET_WCS) {
+		*wp = aes->aes_wcs.s;
+		return (ret);
+	}
+
+	*wp = NULL;
+	/* Try converting MBS to WCS using native locale. */
+	if (aes->aes_set & AES_SET_MBS) {
+		archive_wstring_empty(&(aes->aes_wcs));
+		r = archive_wstring_append_from_mbs(&(aes->aes_wcs),
+		    aes->aes_mbs.s, aes->aes_mbs.length);
+		if (r == 0) {
+			aes->aes_set |= AES_SET_WCS;
+			*wp = aes->aes_wcs.s;
+		} else
+			ret = -1;/* failure. */
+	}
+	return (ret);
+}
+
+int
+archive_mstring_get_mbs_l(struct archive_mstring *aes,
+    const char **p, size_t *length, struct archive_string_conv *sc)
+{
+	int r, ret = 0;
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/*
+	 * Internationalization programing on Windows must use Wide
+	 * characters because Windows platform cannot make locale UTF-8.
+	 */
+	if (sc != NULL && (aes->aes_set & AES_SET_WCS) != 0) {
+		archive_string_empty(&(aes->aes_mbs_in_locale));
+		r = archive_string_append_from_wcs_in_codepage(
+		    &(aes->aes_mbs_in_locale), aes->aes_wcs.s,
+		    aes->aes_wcs.length, sc);
+		if (r == 0) {
+			*p = aes->aes_mbs_in_locale.s;
+			if (length != NULL)
+				*length = aes->aes_mbs_in_locale.length;
+			return (0);
+		} else if (errno == ENOMEM)
+			return (-1);
+		else
+			ret = -1;
+	}
 #endif
+
+	/* If there is not an MBS form but is a WCS form, try converting
+	 * with the native locale to be used for translating it to specified
+	 * character-set. */
+	if ((aes->aes_set & AES_SET_MBS) == 0 &&
+	    (aes->aes_set & AES_SET_WCS) != 0) {
+		archive_string_empty(&(aes->aes_mbs));
+		r = archive_string_append_from_wcs(&(aes->aes_mbs),
+		    aes->aes_wcs.s, aes->aes_wcs.length);
+		if (r == 0)
+			aes->aes_set |= AES_SET_MBS;
+		else if (errno == ENOMEM)
+			return (-1);
+		else
+			ret = -1;
+	}
+	/* If we already have an MBS form, use it to be translated to
+	 * specified character-set. */
+	if (aes->aes_set & AES_SET_MBS) {
+		if (sc == NULL) {
+			/* Conversion is unneeded. */
+			*p = aes->aes_mbs.s;
+			if (length != NULL)
+				*length = aes->aes_mbs.length;
+			return (0);
+		}
+		ret = archive_strncpy_in_locale(&(aes->aes_mbs_in_locale),
+		    aes->aes_mbs.s, aes->aes_mbs.length, sc);
+		*p = aes->aes_mbs_in_locale.s;
+		if (length != NULL)
+			*length = aes->aes_mbs_in_locale.length;
+	} else {
+		*p = NULL;
+		if (length != NULL)
+			*length = 0;
+	}
+	return (ret);
 }
 
-#endif /* _WIN32 && ! __CYGWIN__ */
+int
+archive_mstring_copy_mbs(struct archive_mstring *aes, const char *mbs)
+{
+	if (mbs == NULL) {
+		aes->aes_set = 0;
+		return (0);
+	}
+	return (archive_mstring_copy_mbs_len(aes, mbs, strlen(mbs)));
+}
+
+int
+archive_mstring_copy_mbs_len(struct archive_mstring *aes, const char *mbs,
+    size_t len)
+{
+	if (mbs == NULL) {
+		aes->aes_set = 0;
+		return (0);
+	}
+	aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
+	archive_strncpy(&(aes->aes_mbs), mbs, len);
+	archive_string_empty(&(aes->aes_utf8));
+	archive_wstring_empty(&(aes->aes_wcs));
+	return (0);
+}
+
+int
+archive_mstring_copy_wcs(struct archive_mstring *aes, const wchar_t *wcs)
+{
+	return archive_mstring_copy_wcs_len(aes, wcs, wcs == NULL ? 0 : wcslen(wcs));
+}
+
+int
+archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs,
+    size_t len)
+{
+	if (wcs == NULL) {
+		aes->aes_set = 0;
+	}
+	aes->aes_set = AES_SET_WCS; /* Only WCS form set. */
+	archive_string_empty(&(aes->aes_mbs));
+	archive_string_empty(&(aes->aes_utf8));
+	archive_wstrncpy(&(aes->aes_wcs), wcs, len);
+	return (0);
+}
+
+int
+archive_mstring_copy_mbs_len_l(struct archive_mstring *aes,
+    const char *mbs, size_t len, struct archive_string_conv *sc)
+{
+	int r;
+
+	if (mbs == NULL) {
+		aes->aes_set = 0;
+		return (0);
+	}
+	archive_string_empty(&(aes->aes_mbs));
+	archive_wstring_empty(&(aes->aes_wcs));
+	archive_string_empty(&(aes->aes_utf8));
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/*
+	 * Internationalization programing on Windows must use Wide
+	 * characters because Windows platform cannot make locale UTF-8.
+	 */
+	if (sc == NULL) {
+		if (archive_string_append(&(aes->aes_mbs),
+			mbs, mbsnbytes(mbs, len)) == NULL) {
+			aes->aes_set = 0;
+			r = -1;
+		} else {
+			aes->aes_set = AES_SET_MBS;
+			r = 0;
+		}
+#if defined(HAVE_ICONV)
+	} else if (sc != NULL && sc->cd_w != (iconv_t)-1) {
+		/*
+		 * This case happens only when MultiByteToWideChar() cannot
+		 * handle sc->from_cp, and we have to iconv in order to
+		 * translate character-set to wchar_t,UTF-16.
+		 */
+		iconv_t cd = sc->cd;
+		unsigned from_cp;
+		int flag;
+
+		/*
+		 * Translate multi-bytes from some character-set to UTF-8.
+		 */ 
+		sc->cd = sc->cd_w;
+		r = archive_strncpy_in_locale(&(aes->aes_utf8), mbs, len, sc);
+		sc->cd = cd;
+		if (r != 0) {
+			aes->aes_set = 0;
+			return (r);
+		}
+		aes->aes_set = AES_SET_UTF8;
+
+		/*
+		 * Append the UTF-8 string into wstring.
+		 */ 
+		flag = sc->flag;
+		sc->flag &= ~(SCONV_NORMALIZATION_C
+				| SCONV_TO_UTF16| SCONV_FROM_UTF16);
+		from_cp = sc->from_cp;
+		sc->from_cp = CP_UTF8;
+		r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs),
+			aes->aes_utf8.s, aes->aes_utf8.length, sc);
+		sc->flag = flag;
+		sc->from_cp = from_cp;
+		if (r == 0)
+			aes->aes_set |= AES_SET_WCS;
+#endif
+	} else {
+		r = archive_wstring_append_from_mbs_in_codepage(
+		    &(aes->aes_wcs), mbs, len, sc);
+		if (r == 0)
+			aes->aes_set = AES_SET_WCS;
+		else
+			aes->aes_set = 0;
+	}
+#else
+	r = archive_strncpy_in_locale(&(aes->aes_mbs), mbs, len, sc);
+	if (r == 0)
+		aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
+	else
+		aes->aes_set = 0;
+#endif
+	return (r);
+}
+
+/*
+ * The 'update' form tries to proactively update all forms of
+ * this string (WCS and MBS) and returns an error if any of
+ * them fail.  This is used by the 'pax' handler, for instance,
+ * to detect and report character-conversion failures early while
+ * still allowing clients to get potentially useful values from
+ * the more tolerant lazy conversions.  (get_mbs and get_wcs will
+ * strive to give the user something useful, so you can get hopefully
+ * usable values even if some of the character conversions are failing.)
+ */
+int
+archive_mstring_update_utf8(struct archive *a, struct archive_mstring *aes,
+    const char *utf8)
+{
+	struct archive_string_conv *sc;
+	int r;
+
+	if (utf8 == NULL) {
+		aes->aes_set = 0;
+		return (0); /* Succeeded in clearing everything. */
+	}
+
+	/* Save the UTF8 string. */
+	archive_strcpy(&(aes->aes_utf8), utf8);
+
+	/* Empty the mbs and wcs strings. */
+	archive_string_empty(&(aes->aes_mbs));
+	archive_wstring_empty(&(aes->aes_wcs));
+
+	aes->aes_set = AES_SET_UTF8;	/* Only UTF8 is set now. */
+
+	/* Try converting UTF-8 to MBS, return false on failure. */
+	sc = archive_string_conversion_from_charset(a, "UTF-8", 1);
+	if (sc == NULL)
+		return (-1);/* Couldn't allocate memory for sc. */
+	r = archive_strcpy_in_locale(&(aes->aes_mbs), utf8, sc);
+	if (a == NULL)
+		free_sconv_object(sc);
+	if (r != 0)
+		return (-1);
+	aes->aes_set = AES_SET_UTF8 | AES_SET_MBS; /* Both UTF8 and MBS set. */
+
+	/* Try converting MBS to WCS, return false on failure. */
+	if (archive_wstring_append_from_mbs(&(aes->aes_wcs), aes->aes_mbs.s,
+	    aes->aes_mbs.length))
+		return (-1);
+	aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;
+
+	/* All conversions succeeded. */
+	return (0);
+}
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_string.h
--- a/head/contrib/libarchive/libarchive/archive_string.h	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_string.h	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2003-2010 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -22,13 +22,15 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/contrib/libarchive/libarchive/archive_string.h 228763 2011-12-21 11:13:29Z mm $
+ * $FreeBSD: head/contrib/libarchive/libarchive/archive_string.h 232153 2012-02-25 10:58:02Z mm $
  *
  */
 
 #ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
 #error This header is only to be used internally to libarchive.
 #endif
+#endif
 
 #ifndef ARCHIVE_STRING_H_INCLUDED
 #define	ARCHIVE_STRING_H_INCLUDED
@@ -47,62 +49,97 @@
 #include "archive.h"
 
 /*
- * Basic resizable/reusable string support a la Java's "StringBuffer."
+ * Basic resizable/reusable string support similar to Java's "StringBuffer."
  *
  * Unlike sbuf(9), the buffers here are fully reusable and track the
  * length throughout.
- *
- * Note that all visible symbols here begin with "__archive" as they
- * are internal symbols not intended for anyone outside of this library
- * to see or use.
  */
 
 struct archive_string {
 	char	*s;  /* Pointer to the storage */
-	size_t	 length; /* Length of 's' */
-	size_t	 buffer_length; /* Length of malloc-ed storage */
+	size_t	 length; /* Length of 's' in characters */
+	size_t	 buffer_length; /* Length of malloc-ed storage in bytes. */
 };
 
+struct archive_wstring {
+	wchar_t	*s;  /* Pointer to the storage */
+	size_t	 length; /* Length of 's' in characters */
+	size_t	 buffer_length; /* Length of malloc-ed storage in bytes. */
+};
+
+struct archive_string_conv;
+
 /* Initialize an archive_string object on the stack or elsewhere. */
 #define	archive_string_init(a)	\
 	do { (a)->s = NULL; (a)->length = 0; (a)->buffer_length = 0; } while(0)
 
 /* Append a C char to an archive_string, resizing as necessary. */
 struct archive_string *
-__archive_strappend_char(struct archive_string *, char);
-#define	archive_strappend_char __archive_strappend_char
+archive_strappend_char(struct archive_string *, char);
 
-/* Convert a wide-char string to UTF-8 and append the result. */
-struct archive_string *
-__archive_strappend_w_utf8(struct archive_string *, const wchar_t *);
-#define	archive_strappend_w_utf8	__archive_strappend_w_utf8
+/* Ditto for a wchar_t and an archive_wstring. */
+struct archive_wstring *
+archive_wstrappend_wchar(struct archive_wstring *, wchar_t);
 
-/* Convert a wide-char string to current locale and append the result. */
-/* Returns NULL if conversion fails. */
-struct archive_string *
-__archive_strappend_w_mbs(struct archive_string *, const wchar_t *);
-#define	archive_strappend_w_mbs	__archive_strappend_w_mbs
+/* Convert a Unicode string to current locale and append the result. */
+/* Returns -1 if conversion fails. */
+int
+archive_string_append_from_wcs(struct archive_string *, const wchar_t *, size_t);
 
-/* Basic append operation. */
-struct archive_string *
-__archive_string_append(struct archive_string *as, const char *p, size_t s);
+
+/* Create a string conversion object.
+ * Return NULL and set a error message if the conversion is not supported
+ * on the platform. */
+struct archive_string_conv *
+archive_string_conversion_to_charset(struct archive *, const char *, int);
+struct archive_string_conv *
+archive_string_conversion_from_charset(struct archive *, const char *, int);
+/* Create the default string conversion object for reading/writing an archive.
+ * Return NULL if the conversion is unneeded.
+ * Note: On non Windows platform this always returns NULL.
+ */
+struct archive_string_conv *
+archive_string_default_conversion_for_read(struct archive *);
+struct archive_string_conv *
+archive_string_default_conversion_for_write(struct archive *);
+/* Dispose of a string conversion object. */
+void
+archive_string_conversion_free(struct archive *);
+const char *
+archive_string_conversion_charset_name(struct archive_string_conv *);
+void
+archive_string_conversion_set_opt(struct archive_string_conv *, int);
+#define SCONV_SET_OPT_UTF8_LIBARCHIVE2X	1
+
+
+/* Copy one archive_string to another in locale conversion.
+ * Return -1 if conversion failes. */
+int
+archive_strncpy_in_locale(struct archive_string *, const void *, size_t,
+    struct archive_string_conv *);
+
+/* Copy one archive_string to another in locale conversion.
+ * Return -1 if conversion failes. */
+int
+archive_strncat_in_locale(struct archive_string *, const void *, size_t,
+    struct archive_string_conv *);
+
 
 /* Copy one archive_string to another */
-void
-__archive_string_copy(struct archive_string *dest, struct archive_string *src);
-#define archive_string_copy(dest, src) \
-	__archive_string_copy(dest, src)
+#define	archive_string_copy(dest, src) \
+	((dest)->length = 0, archive_string_concat((dest), (src)))
+#define	archive_wstring_copy(dest, src) \
+	((dest)->length = 0, archive_wstring_concat((dest), (src)))
 
 /* Concatenate one archive_string to another */
-void
-__archive_string_concat(struct archive_string *dest, struct archive_string *src);
-#define archive_string_concat(dest, src) \
-	__archive_string_concat(dest, src)
+void archive_string_concat(struct archive_string *dest, struct archive_string *src);
+void archive_wstring_concat(struct archive_wstring *dest, struct archive_wstring *src);
 
 /* Ensure that the underlying buffer is at least as large as the request. */
 struct archive_string *
-__archive_string_ensure(struct archive_string *, size_t);
-#define	archive_string_ensure __archive_string_ensure
+archive_string_ensure(struct archive_string *, size_t);
+struct archive_wstring *
+archive_wstring_ensure(struct archive_wstring *, size_t);
 
 /* Append C string, which may lack trailing \0. */
 /* The source is declared void * here because this gets used with
@@ -110,42 +147,91 @@
  * Declaring it "char *" as with some of the other functions just
  * leads to a lot of extra casts. */
 struct archive_string *
-__archive_strncat(struct archive_string *, const void *, size_t);
-#define	archive_strncat  __archive_strncat
+archive_strncat(struct archive_string *, const void *, size_t);
+struct archive_wstring *
+archive_wstrncat(struct archive_wstring *, const wchar_t *, size_t);
 
 /* Append a C string to an archive_string, resizing as necessary. */
-#define	archive_strcat(as,p) __archive_string_append((as),(p),strlen(p))
+struct archive_string *
+archive_strcat(struct archive_string *, const void *);
+struct archive_wstring *
+archive_wstrcat(struct archive_wstring *, const wchar_t *);
 
 /* Copy a C string to an archive_string, resizing as necessary. */
 #define	archive_strcpy(as,p) \
-	((as)->length = 0, __archive_string_append((as), (p), p == NULL ? 0 : strlen(p)))
+	archive_strncpy((as), (p), ((p) == NULL ? 0 : strlen(p)))
+#define	archive_wstrcpy(as,p) \
+	archive_wstrncpy((as), (p), ((p) == NULL ? 0 : wcslen(p)))
+#define	archive_strcpy_in_locale(as,p,lo) \
+	archive_strncpy_in_locale((as), (p), ((p) == NULL ? 0 : strlen(p)), (lo))
 
 /* Copy a C string to an archive_string with limit, resizing as necessary. */
 #define	archive_strncpy(as,p,l) \
 	((as)->length=0, archive_strncat((as), (p), (l)))
+#define	archive_wstrncpy(as,p,l) \
+	((as)->length = 0, archive_wstrncat((as), (p), (l)))
 
 /* Return length of string. */
 #define	archive_strlen(a) ((a)->length)
 
 /* Set string length to zero. */
 #define	archive_string_empty(a) ((a)->length = 0)
+#define	archive_wstring_empty(a) ((a)->length = 0)
 
 /* Release any allocated storage resources. */
-void	__archive_string_free(struct archive_string *);
-#define	archive_string_free  __archive_string_free
+void	archive_string_free(struct archive_string *);
+void	archive_wstring_free(struct archive_wstring *);
 
 /* Like 'vsprintf', but resizes the underlying string as necessary. */
-void	__archive_string_vsprintf(struct archive_string *, const char *,
+/* Note: This only implements a small subset of standard printf functionality. */
+void	archive_string_vsprintf(struct archive_string *, const char *,
 	    va_list) __LA_PRINTF(2, 0);
-#define	archive_string_vsprintf	__archive_string_vsprintf
+void	archive_string_sprintf(struct archive_string *, const char *, ...)
+	    __LA_PRINTF(2, 3);
 
-void	__archive_string_sprintf(struct archive_string *, const char *, ...)
-	    __LA_PRINTF(2, 3);
-#define	archive_string_sprintf	__archive_string_sprintf
+/* Translates from MBS to Unicode. */
+/* Returns non-zero if conversion failed in any way. */
+int archive_wstring_append_from_mbs(struct archive_wstring *dest,
+    const char *, size_t);
 
-/* Allocates a fresh buffer and converts as (assumed to be UTF-8) into it.
- * Returns NULL if conversion failed in any way. */
-wchar_t *__archive_string_utf8_w(struct archive_string *as);
+
+/* A "multistring" can hold Unicode, UTF8, or MBS versions of
+ * the string.  If you set and read the same version, no translation
+ * is done.  If you set and read different versions, the library
+ * will attempt to transparently convert.
+ */
+struct archive_mstring {
+	struct archive_string aes_mbs;
+	struct archive_string aes_utf8;
+	struct archive_wstring aes_wcs;
+	struct archive_string aes_mbs_in_locale;
+	/* Bitmap of which of the above are valid.  Because we're lazy
+	 * about malloc-ing and reusing the underlying storage, we
+	 * can't rely on NULL pointers to indicate whether a string
+	 * has been set. */
+	int aes_set;
+#define	AES_SET_MBS 1
+#define	AES_SET_UTF8 2
+#define	AES_SET_WCS 4
+};
+
+void	archive_mstring_clean(struct archive_mstring *);
+void	archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src);
+int archive_mstring_get_mbs(struct archive *, struct archive_mstring *, const char **);
+int archive_mstring_get_utf8(struct archive *, struct archive_mstring *, const char **);
+int archive_mstring_get_wcs(struct archive *, struct archive_mstring *, const wchar_t **);
+int	archive_mstring_get_mbs_l(struct archive_mstring *, const char **,
+	    size_t *, struct archive_string_conv *);
+int	archive_mstring_copy_mbs(struct archive_mstring *, const char *mbs);
+int	archive_mstring_copy_mbs_len(struct archive_mstring *, const char *mbs,
+	    size_t);
+int	archive_mstring_copy_utf8(struct archive_mstring *, const char *utf8);
+int	archive_mstring_copy_wcs(struct archive_mstring *, const wchar_t *wcs);
+int	archive_mstring_copy_wcs_len(struct archive_mstring *,
+	    const wchar_t *wcs, size_t);
+int	archive_mstring_copy_mbs_len_l(struct archive_mstring *,
+	    const char *mbs, size_t, struct archive_string_conv *);
+int     archive_mstring_update_utf8(struct archive *, struct archive_mstring *aes, const char *utf8);
 
 
 #endif
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_string_sprintf.c
--- a/head/contrib/libarchive/libarchive/archive_string_sprintf.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_string_sprintf.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_string_sprintf.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_string_sprintf.c 232153 2012-02-25 10:58:02Z mm $");
 
 /*
  * The use of printf()-family functions can be troublesome
@@ -60,16 +60,19 @@
 static void
 append_int(struct archive_string *as, intmax_t d, unsigned base)
 {
+	uintmax_t ud;
+
 	if (d < 0) {
 		archive_strappend_char(as, '-');
-		d = -d;
-	}
-	append_uint(as, d, base);
+		ud = (d == INTMAX_MIN) ? (uintmax_t)(INTMAX_MAX) + 1 : (uintmax_t)(-d);
+	} else
+		ud = d;
+	append_uint(as, ud, base);
 }
 
 
 void
-__archive_string_sprintf(struct archive_string *as, const char *fmt, ...)
+archive_string_sprintf(struct archive_string *as, const char *fmt, ...)
 {
 	va_list ap;
 
@@ -83,15 +86,16 @@
  * necessary.
  */
 void
-__archive_string_vsprintf(struct archive_string *as, const char *fmt,
+archive_string_vsprintf(struct archive_string *as, const char *fmt,
     va_list ap)
 {
 	char long_flag;
 	intmax_t s; /* Signed integer temp. */
 	uintmax_t u; /* Unsigned integer temp. */
 	const char *p, *p2;
+	const wchar_t *pw;
 
-	if (__archive_string_ensure(as, 64) == NULL)
+	if (archive_string_ensure(as, 64) == NULL)
 		__archive_errx(1, "Out of memory");
 
 	if (fmt == NULL) {
@@ -112,40 +116,58 @@
 		long_flag = '\0';
 		switch(*p) {
 		case 'j':
-			long_flag = 'j';
-			p++;
-			break;
 		case 'l':
-			long_flag = 'l';
+		case 'z':
+			long_flag = *p;
 			p++;
 			break;
 		}
 
 		switch (*p) {
 		case '%':
-			__archive_strappend_char(as, '%');
+			archive_strappend_char(as, '%');
 			break;
 		case 'c':
 			s = va_arg(ap, int);
-			__archive_strappend_char(as, s);
+			archive_strappend_char(as, s);
 			break;
 		case 'd':
 			switch(long_flag) {
 			case 'j': s = va_arg(ap, intmax_t); break;
 			case 'l': s = va_arg(ap, long); break;
+			case 'z': s = va_arg(ap, ssize_t); break;
 			default:  s = va_arg(ap, int); break;
 			}
 		        append_int(as, s, 10);
 			break;
 		case 's':
-			p2 = va_arg(ap, char *);
-			archive_strcat(as, p2);
+			switch(long_flag) {
+			case 'l':
+				pw = va_arg(ap, wchar_t *);
+				if (pw == NULL)
+					pw = L"(null)";
+				archive_string_append_from_wcs(as, pw, wcslen(pw));
+				break;
+			default:
+				p2 = va_arg(ap, char *);
+				if (p2 == NULL)
+					p2 = "(null)";
+				archive_strcat(as, p2);
+				break;
+			}
+			break;
+		case 'S':
+			pw = va_arg(ap, wchar_t *);
+			if (pw == NULL)
+				pw = L"(null)";
+			archive_string_append_from_wcs(as, pw, wcslen(pw));
 			break;
 		case 'o': case 'u': case 'x': case 'X':
 			/* Common handling for unsigned integer formats. */
 			switch(long_flag) {
 			case 'j': u = va_arg(ap, uintmax_t); break;
 			case 'l': u = va_arg(ap, unsigned long); break;
+			case 'z': u = va_arg(ap, size_t); break;
 			default:  u = va_arg(ap, unsigned int); break;
 			}
 			/* Format it in the correct base. */
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_util.3
--- a/head/contrib/libarchive/libarchive/archive_util.3	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_util.3	Fri Mar 02 16:54:40 2012 +0200
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: head/contrib/libarchive/libarchive/archive_util.3 228773 2011-12-21 15:18:52Z mm $
+.\" $FreeBSD: head/contrib/libarchive/libarchive/archive_util.3 232153 2012-02-25 10:58:02Z mm $
 .\"
 .Dd January 8, 2005
 .Dt ARCHIVE_UTIL 3
@@ -35,8 +35,12 @@
 .Nm archive_errno ,
 .Nm archive_error_string ,
 .Nm archive_file_count ,
+.Nm archive_filter_code ,
+.Nm archive_filter_count ,
+.Nm archive_filter_name ,
 .Nm archive_format ,
 .Nm archive_format_name ,
+.Nm archive_position ,
 .Nm archive_set_error
 .Nd libarchive utility functions
 .Sh SYNOPSIS
@@ -56,9 +60,17 @@
 .Ft int
 .Fn archive_file_count "struct archive *"
 .Ft int
+.Fn archive_filter_code "struct archive *" "int"
+.Ft int
+.Fn archive_filter_count "struct archive *" "int"
+.Ft const char *
+.Fn archive_filter_name "struct archive *" "int"
+.Ft int
 .Fn archive_format "struct archive *"
 .Ft const char *
 .Fn archive_format_name "struct archive *"
+.Ft int64_t
+.Fn archive_position "struct archive *" "int"
 .Ft void
 .Fo archive_set_error
 .Fa "struct archive *"
@@ -77,17 +89,21 @@
 Clears any error information left over from a previous call.
 Not generally used in client code.
 .It Fn archive_compression
-Returns a numeric code indicating the current compression.
-This value is set by
-.Fn archive_read_open .
+Synonym for
+.Fn archive_filter_code(a, 0) .
 .It Fn archive_compression_name
-Returns a text description of the current compression suitable for display.
+Synonym for
+.Fn archive_filter_name(a, 0) .
 .It Fn archive_copy_error
 Copies error information from one archive to another.
 .It Fn archive_errno
 Returns a numeric error code (see
 .Xr errno 2 )
 indicating the reason for the most recent error return.
+Note that this can not be reliably used to detect whether an
+error has occurred.
+It should be used only after another libarchive function
+has returned an error status.
 .It Fn archive_error_string
 Returns a textual error message suitable for display.
 The error message here is usually more specific than that
@@ -98,9 +114,45 @@
 .It Fn archive_file_count
 Returns a count of the number of files processed by this archive object.
 The count is incremented by calls to
-.Xr archive_write_header
+.Xr archive_write_header 3
 or
-.Xr archive_read_next_header .
+.Xr archive_read_next_header 3 .
+.It Fn archive_filter_code
+Returns a numeric code identifying the indicated filter.
+See
+.Fn archive_filter_count
+for details of the numbering.
+.It Fn archive_filter_count
+Returns the number of filters in the current pipeline.
+For read archive handles, these filters are added automatically
+by the automatic format detection.
+For write archive handles, these filters are added by calls to the various
+.Fn archive_write_add_filter_XXX
+functions.
+Filters in the resulting pipeline are numbered so that filter 0
+is the filter closest to the format handler.
+As a convenience, functions that expect a filter number will
+accept -1 as a synonym for the highest-numbered filter.
+.Pp
+For example, when reading a uuencoded gzipped tar archive, there
+are three filters:
+filter 0 is the gunzip filter,
+filter 1 is the uudecode filter,
+and filter 2 is the pseudo-filter that wraps the archive read functions.
+In this case, requesting
+.Fn archive_position(a, -1)
+would be a synonym for
+.Fn archive_position(a, 2)
+which would return the number of bytes currently read from the archive, while
+.Fn archive_position(a, 1)
+would return the number of bytes after uudecoding, and
+.Fn archive_position(a, 0)
+would return the number of bytes after decompression.
+.It Fn archive_filter_name
+Returns a textual name identifying the indicated filter.
+See
+.Fn archive_filter_count
+for details of the numbering.
 .It Fn archive_format
 Returns a numeric code indicating the format of the current
 archive entry.
@@ -113,6 +165,16 @@
 These entries will have different format codes.
 .It Fn archive_format_name
 A textual description of the format of the current entry.
+.It Fn archive_position
+Returns the number of bytes read from or written to the indicated filter.
+In particular,
+.Fn archive_position(a, 0)
+returns the number of bytes read or written by the format handler, while
+.Fn archive_position(a, -1)
+returns the number of bytes read or written to the archive.
+See
+.Fn archive_filter_count
+for details of the numbering here.
 .It Fn archive_set_error
 Sets the numeric error code and error description that will be returned
 by
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_util.c
--- a/head/contrib/libarchive/libarchive/archive_util.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_util.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2009 Michihiro NAKAJIMA
+ * Copyright (c) 2009,2010 Michihiro NAKAJIMA
  * Copyright (c) 2003-2007 Tim Kientzle
  * All rights reserved.
  *
@@ -25,53 +25,39 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_util.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_util.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
+#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)
+#include <wincrypt.h>
+#endif
 
 #include "archive.h"
 #include "archive_private.h"
 #include "archive_string.h"
 
-#if ARCHIVE_VERSION_NUMBER < 3000000
-/* These disappear in libarchive 3.0 */
-/* Deprecated. */
+/* Generic initialization of 'struct archive' objects. */
 int
-archive_api_feature(void)
+__archive_clean(struct archive *a)
 {
-	return (ARCHIVE_API_FEATURE);
+	archive_string_conversion_free(a);
+	return (ARCHIVE_OK);
 }
 
-/* Deprecated. */
-int
-archive_api_version(void)
-{
-	return (ARCHIVE_API_VERSION);
-}
-
-/* Deprecated synonym for archive_version_number() */
-int
-archive_version_stamp(void)
-{
-	return (archive_version_number());
-}
-
-/* Deprecated synonym for archive_version_string() */
-const char *
-archive_version(void)
-{
-	return (archive_version_string());
-}
-#endif
-
 int
 archive_version_number(void)
 {
@@ -97,7 +83,7 @@
 	if (a->error != NULL  &&  *a->error != '\0')
 		return (a->error);
 	else
-		return ("(Empty error message)");
+		return (NULL);
 }
 
 int
@@ -122,13 +108,13 @@
 int
 archive_compression(struct archive *a)
 {
-	return (a->compression_code);
+	return archive_filter_code(a, 0);
 }
 
 const char *
 archive_compression_name(struct archive *a)
 {
-	return (a->compression_name);
+	return archive_filter_name(a, 0);
 }
 
 
@@ -138,7 +124,7 @@
 int64_t
 archive_position_compressed(struct archive *a)
 {
-	return (a->raw_position);
+	return archive_filter_bytes(a, -1);
 }
 
 /*
@@ -147,7 +133,7 @@
 int64_t
 archive_position_uncompressed(struct archive *a)
 {
-	return (a->file_position);
+	return archive_filter_bytes(a, 0);
 }
 
 void
@@ -169,6 +155,7 @@
 		return;
 	}
 
+	archive_string_empty(&(a->error_string));
 	va_start(ap, fmt);
 	archive_string_vsprintf(&(a->error_string), fmt, ap);
 	va_end(ap);
@@ -200,193 +187,279 @@
 }
 
 /*
- * Parse option strings
- *  Detail of option format.
- *    - The option can accept:
- *     "opt-name", "!opt-name", "opt-name=value".
- *
- *    - The option entries are separated by comma.
- *        e.g  "compression=9,opt=XXX,opt-b=ZZZ"
- *
- *    - The name of option string consist of '-' and alphabet
- *      but character '-' cannot be used for the first character.
- *      (Regular expression is [a-z][-a-z]+)
- *
- *    - For a specfic format/filter, using the format name with ':'.
- *        e.g  "zip:compression=9"
- *        (This "compression=9" option entry is for "zip" format only)
- *
- *      If another entries follow it, those are not for
- *      the specfic format/filter.
- *        e.g  handle "zip:compression=9,opt=XXX,opt-b=ZZZ"
- *          "zip" format/filter handler will get "compression=9"
- *          all format/filter handler will get "opt=XXX"
- *          all format/filter handler will get "opt-b=ZZZ"
- *
- *    - Whitespace and tab are bypassed.
- *
+ * Create a temporary file
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Do not use Windows tmpfile() function.
+ * It will make a temporary file under the root directory
+ * and it'll cause permission error if a user who is
+ * non-Administrator creates temporary files.
+ * Also Windows version of mktemp family including _mktemp_s
+ * are not secure.
  */
 int
-__archive_parse_options(const char *p, const char *fn, int keysize, char *key,
-    int valsize, char *val)
+__archive_mktemp(const char *tmpdir)
 {
-	const char *p_org;
-	int apply;
-	int kidx, vidx;
-	int negative; 
-	enum {
-		/* Requested for initialization. */
-		INIT,
-		/* Finding format/filter-name and option-name. */
-		F_BOTH,
-		/* Finding option-name only.
-		 * (already detected format/filter-name) */
-		F_NAME,
-		/* Getting option-value. */
-		G_VALUE,
-	} state;
+	static const wchar_t num[] = {
+		L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
+		L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
+		L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
+		L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
+		L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
+		L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
+		L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
+		L'u', L'v', L'w', L'x', L'y', L'z'
+	};
+	HCRYPTPROV hProv;
+	struct archive_wstring temp_name;
+	wchar_t *ws;
+	DWORD attr;
+	wchar_t *xp, *ep;
+	int fd;
 
-	p_org = p;
-	state = INIT;
-	kidx = vidx = negative = 0;
-	apply = 1;
-	while (*p) {
-		switch (state) {
-		case INIT:
-			kidx = vidx = 0;
-			negative = 0;
-			apply = 1;
-			state = F_BOTH;
-			break;
-		case F_BOTH:
-		case F_NAME:
-			if ((*p >= 'a' && *p <= 'z') ||
-			    (*p >= '0' && *p <= '9') || *p == '-') {
-				if (kidx == 0 && !(*p >= 'a' && *p <= 'z'))
-					/* Illegal sequence. */
-					return (-1);
-				if (kidx >= keysize -1)
-					/* Too many characters. */
-					return (-1);
-				key[kidx++] = *p++;
-			} else if (*p == '!') {
-				if (kidx != 0)
-					/* Illegal sequence. */
-					return (-1);
-				negative = 1;
-				++p;
-			} else if (*p == ',') {
-				if (kidx == 0)
-					/* Illegal sequence. */
-					return (-1);
-				if (!negative)
-					val[vidx++] = '1';
-				/* We have got boolean option data. */
-				++p;
-				if (apply)
-					goto complete;
-				else
-					/* This option does not apply to the
-					 * format which the fn variable
-					 * indicate. */
-					state = INIT;
-			} else if (*p == ':') {
-				/* obuf data is format name */
-				if (state == F_NAME)
-					/* We already found it. */
-					return (-1);
-				if (kidx == 0)
-					/* Illegal sequence. */
-					return (-1);
-				if (negative)
-					/* We cannot accept "!format-name:". */
-					return (-1);
-				key[kidx] = '\0';
-				if (strcmp(fn, key) != 0)
-					/* This option does not apply to the
-					 * format which the fn variable
-					 * indicate. */
-					apply = 0;
-				kidx = 0;
-				++p;
-				state = F_NAME;
-			} else if (*p == '=') {
-				if (kidx == 0)
-					/* Illegal sequence. */
-					return (-1);
-				if (negative)
-					/* We cannot accept "!opt-name=value". */
-					return (-1);
-				++p;
-				state = G_VALUE;
-			} else if (*p == ' ') {
-				/* Pass the space character */
-				++p;
-			} else {
-				/* Illegal character. */
-				return (-1);
-			}
-			break;
-		case G_VALUE:
-			if (*p == ',') {
-				if (vidx == 0)
-					/* Illegal sequence. */
-					return (-1);
-				/* We have got option data. */
-				++p;
-				if (apply)
-					goto complete;
-				else
-					/* This option does not apply to the
-					 * format which the fn variable
-					 * indicate. */
-					state = INIT;
-			} else if (*p == ' ') {
-				/* Pass the space character */
-				++p;
-			} else {
-				if (vidx >= valsize -1)
-					/* Too many characters. */
-					return (-1);
-				val[vidx++] = *p++;
-			}
-			break;
-		} 
+	hProv = (HCRYPTPROV)NULL;
+	fd = -1;
+	ws = NULL;
+	archive_string_init(&temp_name);
+
+	/* Get a temporary directory. */
+	if (tmpdir == NULL) {
+		size_t l;
+		wchar_t *tmp;
+
+		l = GetTempPathW(0, NULL);
+		if (l == 0) {
+			la_dosmaperr(GetLastError());
+			goto exit_tmpfile;
+		}
+		tmp = malloc(l*sizeof(wchar_t));
+		if (tmp == NULL) {
+			errno = ENOMEM;
+			goto exit_tmpfile;
+		}
+		GetTempPathW(l, tmp);
+		archive_wstrcpy(&temp_name, tmp);
+		free(tmp);
+	} else {
+		archive_wstring_append_from_mbs(&temp_name, tmpdir,
+		    strlen(tmpdir));
+		if (temp_name.s[temp_name.length-1] != L'/')
+			archive_wstrappend_wchar(&temp_name, L'/');
 	}
 
-	switch (state) {
-	case F_BOTH:
-	case F_NAME:
-		if (kidx != 0) {
-			if (!negative)
-				val[vidx++] = '1';
-			/* We have got boolean option. */
-			if (apply)
-				/* This option apply to the format which the
-				 * fn variable indicate. */
-				goto complete;
+	/* Check if temp_name is a directory. */
+	attr = GetFileAttributesW(temp_name.s);
+	if (attr == (DWORD)-1) {
+		if (GetLastError() != ERROR_FILE_NOT_FOUND) {
+			la_dosmaperr(GetLastError());
+			goto exit_tmpfile;
 		}
-		break;
-	case G_VALUE:
-		if (vidx == 0)
-			/* Illegal sequence. */
-			return (-1);
-		/* We have got option value. */
-		if (apply)
-			/* This option apply to the format which the fn
-			 * variable indicate. */
-			goto complete;
-		break;
-	case INIT:/* nothing */
-		break;
+		ws = __la_win_permissive_name_w(temp_name.s);
+		if (ws == NULL) {
+			errno = EINVAL;
+			goto exit_tmpfile;
+		}
+		attr = GetFileAttributesW(ws);
+		if (attr == (DWORD)-1) {
+			la_dosmaperr(GetLastError());
+			goto exit_tmpfile;
+		}
+	}
+	if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
+		errno = ENOTDIR;
+		goto exit_tmpfile;
 	}
 
-	/* End of Option string. */
-	return (0);
+	/*
+	 * Create a temporary file.
+	 */
+	archive_wstrcat(&temp_name, L"libarchive_");
+	xp = temp_name.s + archive_strlen(&temp_name);
+	archive_wstrcat(&temp_name, L"XXXXXXXXXX");
+	ep = temp_name.s + archive_strlen(&temp_name);
 
-complete:
-	key[kidx] = '\0';
-	val[vidx] = '\0';
-	/* Return a size which we've consumed for detecting option */
-	return ((int)(p - p_org));
+	if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
+		CRYPT_VERIFYCONTEXT)) {
+		la_dosmaperr(GetLastError());
+		goto exit_tmpfile;
+	}
+
+	for (;;) {
+		wchar_t *p;
+		HANDLE h;
+
+		/* Generate a random file name through CryptGenRandom(). */
+		p = xp;
+		if (!CryptGenRandom(hProv, (ep - p)*sizeof(wchar_t), (BYTE*)p)) {
+			la_dosmaperr(GetLastError());
+			goto exit_tmpfile;
+		}
+		for (; p < ep; p++)
+			*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
+
+		free(ws);
+		ws = __la_win_permissive_name_w(temp_name.s);
+		if (ws == NULL) {
+			errno = EINVAL;
+			goto exit_tmpfile;
+		}
+		/* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to
+		 * delete this temporary file immediately when this
+		 * file closed. */
+		h = CreateFileW(ws,
+		    GENERIC_READ | GENERIC_WRITE | DELETE,
+		    0,/* Not share */
+		    NULL,
+		    CREATE_NEW,/* Create a new file only */
+		    FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
+		    NULL);
+		if (h == INVALID_HANDLE_VALUE) {
+			/* The same file already exists. retry with
+			 * a new filename. */
+			if (GetLastError() == ERROR_FILE_EXISTS)
+				continue;
+			/* Otherwise, fail creation temporary file. */
+			la_dosmaperr(GetLastError());
+			goto exit_tmpfile;
+		}
+		fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
+		if (fd == -1) {
+			CloseHandle(h);
+			goto exit_tmpfile;
+		} else
+			break;/* success! */
+	}
+exit_tmpfile:
+	if (hProv != (HCRYPTPROV)NULL)
+		CryptReleaseContext(hProv, 0);
+	free(ws);
+	archive_wstring_free(&temp_name);
+	return (fd);
 }
+
+#else
+
+static int
+get_tempdir(struct archive_string *temppath)
+{
+	const char *tmp;
+
+	tmp = getenv("TMPDIR");
+	if (tmp == NULL)
+#ifdef _PATH_TMP
+		tmp = _PATH_TMP;
+#else
+                tmp = "/tmp";
+#endif
+	archive_strcpy(temppath, tmp);
+	if (temppath->s[temppath->length-1] != '/')
+		archive_strappend_char(temppath, '/');
+	return (ARCHIVE_OK);
+}
+
+#if defined(HAVE_MKSTEMP)
+
+/*
+ * We can use mkstemp().
+ */
+
+int
+__archive_mktemp(const char *tmpdir)
+{
+	struct archive_string temp_name;
+	int fd = -1;
+
+	archive_string_init(&temp_name);
+	if (tmpdir == NULL) {
+		if (get_tempdir(&temp_name) != ARCHIVE_OK)
+			goto exit_tmpfile;
+	} else {
+		archive_strcpy(&temp_name, tmpdir);
+		if (temp_name.s[temp_name.length-1] != '/')
+			archive_strappend_char(&temp_name, '/');
+	}
+	archive_strcat(&temp_name, "libarchive_XXXXXX");
+	fd = mkstemp(temp_name.s);
+	if (fd < 0)
+		goto exit_tmpfile;
+	unlink(temp_name.s);
+exit_tmpfile:
+	archive_string_free(&temp_name);
+	return (fd);
+}
+
+#else
+
+/*
+ * We use a private routine.
+ */
+
+int
+__archive_mktemp(const char *tmpdir)
+{
+        static const char num[] = {
+		'0', '1', '2', '3', '4', '5', '6', '7',
+		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
+		'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+		'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+		'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+		'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+		'u', 'v', 'w', 'x', 'y', 'z'
+        };
+	struct archive_string temp_name;
+	struct stat st;
+	int fd;
+	char *tp, *ep;
+	unsigned seed;
+
+	fd = -1;
+	archive_string_init(&temp_name);
+	if (tmpdir == NULL) {
+		if (get_tempdir(&temp_name) != ARCHIVE_OK)
+			goto exit_tmpfile;
+	} else
+		archive_strcpy(&temp_name, tmpdir);
+	if (temp_name.s[temp_name.length-1] == '/') {
+		temp_name.s[temp_name.length-1] = '\0';
+		temp_name.length --;
+	}
+	if (stat(temp_name.s, &st) < 0)
+		goto exit_tmpfile;
+	if (!S_ISDIR(st.st_mode)) {
+		errno = ENOTDIR;
+		goto exit_tmpfile;
+	}
+	archive_strcat(&temp_name, "/libarchive_");
+	tp = temp_name.s + archive_strlen(&temp_name);
+	archive_strcat(&temp_name, "XXXXXXXXXX");
+	ep = temp_name.s + archive_strlen(&temp_name);
+
+	fd = open("/dev/random", O_RDONLY);
+	if (fd < 0)
+		seed = time(NULL);
+	else {
+		if (read(fd, &seed, sizeof(seed)) < 0)
+			seed = time(NULL);
+		close(fd);
+	}
+	do {
+		char *p;
+
+		p = tp;
+		while (p < ep)
+			*p++ = num[((unsigned)rand_r(&seed)) % sizeof(num)];
+		fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR, 0600);
+	} while (fd < 0 && errno == EEXIST);
+	if (fd < 0)
+		goto exit_tmpfile;
+	unlink(temp_name.s);
+exit_tmpfile:
+	archive_string_free(&temp_name);
+	return (fd);
+}
+
+#endif /* HAVE_MKSTEMP */
+#endif /* !_WIN32 || __CYGWIN__ */
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_virtual.c
--- a/head/contrib/libarchive/libarchive/archive_virtual.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_virtual.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,13 +24,37 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_virtual.c 228773 2011-12-21 15:18:52Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_virtual.c 232153 2012-02-25 10:58:02Z mm $");
 
 #include "archive.h"
 #include "archive_entry.h"
 #include "archive_private.h"
 
 int
+archive_filter_code(struct archive *a, int n)
+{
+	return ((a->vtable->archive_filter_code)(a, n));
+}
+
+int
+archive_filter_count(struct archive *a)
+{
+	return ((a->vtable->archive_filter_count)(a));
+}
+
+const char *
+archive_filter_name(struct archive *a, int n)
+{
+	return ((a->vtable->archive_filter_name)(a, n));
+}
+
+int64_t
+archive_filter_bytes(struct archive *a, int n)
+{
+	return ((a->vtable->archive_filter_bytes)(a, n));
+}
+
+int
 archive_write_close(struct archive *a)
 {
 	return ((a->vtable->archive_close)(a));
@@ -45,6 +69,8 @@
 int
 archive_write_free(struct archive *a)
 {
+	if (a == NULL)
+		return (ARCHIVE_OK);
 	return ((a->vtable->archive_free)(a));
 }
 
@@ -53,13 +79,15 @@
 int
 archive_write_finish(struct archive *a)
 {
-	return ((a->vtable->archive_free)(a));
+	return archive_write_free(a);
 }
 #endif
 
 int
 archive_read_free(struct archive *a)
 {
+	if (a == NULL)
+		return (ARCHIVE_OK);
 	return ((a->vtable->archive_free)(a));
 }
 
@@ -68,7 +96,7 @@
 int
 archive_read_finish(struct archive *a)
 {
-	return ((a->vtable->archive_free)(a));
+	return archive_read_free(a);
 }
 #endif
 
@@ -92,7 +120,32 @@
 }
 
 ssize_t
-archive_write_data_block(struct archive *a, const void *buff, size_t s, off_t o)
+archive_write_data_block(struct archive *a, const void *buff, size_t s, int64_t o)
 {
+	if (a->vtable->archive_write_data_block == NULL) {
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "archive_write_data_block not supported");
+		a->state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
 	return ((a->vtable->archive_write_data_block)(a, buff, s, o));
 }
+
+int
+archive_read_next_header(struct archive *a, struct archive_entry **entry)
+{
+	return ((a->vtable->archive_read_next_header)(a, entry));
+}
+
+int
+archive_read_next_header2(struct archive *a, struct archive_entry *entry)
+{
+	return ((a->vtable->archive_read_next_header2)(a, entry));
+}
+
+int
+archive_read_data_block(struct archive *a,
+    const void **buff, size_t *s, int64_t *o)
+{
+	return ((a->vtable->archive_read_data_block)(a, buff, s, o));
+}
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write.3
--- a/head/contrib/libarchive/libarchive/archive_write.3	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write.3	Fri Mar 02 16:54:40 2012 +0200
@@ -1,4 +1,4 @@
-.\" Copyright (c) 2003-2007 Tim Kientzle
+.\" Copyright (c) 2003-2011 Tim Kientzle
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -22,110 +22,16 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: head/contrib/libarchive/libarchive/archive_write.3 228773 2011-12-21 15:18:52Z mm $
+.\" $FreeBSD: head/contrib/libarchive/libarchive/archive_write.3 232153 2012-02-25 10:58:02Z mm $
 .\"
-.Dd May 11, 2008
+.Dd March 23, 2011
 .Dt ARCHIVE_WRITE 3
 .Os
 .Sh NAME
-.Nm archive_write_new ,
-.Nm archive_write_set_format_cpio ,
-.Nm archive_write_set_format_pax ,
-.Nm archive_write_set_format_pax_restricted ,
-.Nm archive_write_set_format_shar ,
-.Nm archive_write_set_format_shar_binary ,
-.Nm archive_write_set_format_ustar ,
-.Nm archive_write_get_bytes_per_block ,
-.Nm archive_write_set_bytes_per_block ,
-.Nm archive_write_set_bytes_in_last_block ,
-.Nm archive_write_set_compression_bzip2 ,
-.Nm archive_write_set_compression_compress ,
-.Nm archive_write_set_compression_gzip ,
-.Nm archive_write_set_compression_none ,
-.Nm archive_write_set_compression_program ,
-.Nm archive_write_set_compressor_options ,
-.Nm archive_write_set_format_options ,
-.Nm archive_write_set_options ,
-.Nm archive_write_open ,
-.Nm archive_write_open_fd ,
-.Nm archive_write_open_FILE ,
-.Nm archive_write_open_filename ,
-.Nm archive_write_open_memory ,
-.Nm archive_write_header ,
-.Nm archive_write_data ,
-.Nm archive_write_finish_entry ,
-.Nm archive_write_close ,
-.Nm archive_write_free
+.Nm archive_write
 .Nd functions for creating archives
 .Sh SYNOPSIS
 .In archive.h
-.Ft struct archive *
-.Fn archive_write_new "void"
-.Ft int
-.Fn archive_write_get_bytes_per_block "struct archive *"
-.Ft int
-.Fn archive_write_set_bytes_per_block "struct archive *" "int bytes_per_block"
-.Ft int
-.Fn archive_write_set_bytes_in_last_block "struct archive *" "int"
-.Ft int
-.Fn archive_write_set_compression_bzip2 "struct archive *"
-.Ft int
-.Fn archive_write_set_compression_compress "struct archive *"
-.Ft int
-.Fn archive_write_set_compression_gzip "struct archive *"
-.Ft int
-.Fn archive_write_set_compression_none "struct archive *"
-.Ft int
-.Fn archive_write_set_compression_program "struct archive *" "const char * cmd"
-.Ft int
-.Fn archive_write_set_format_cpio "struct archive *"
-.Ft int
-.Fn archive_write_set_format_pax "struct archive *"
-.Ft int
-.Fn archive_write_set_format_pax_restricted "struct archive *"
-.Ft int
-.Fn archive_write_set_format_shar "struct archive *"
-.Ft int
-.Fn archive_write_set_format_shar_binary "struct archive *"
-.Ft int
-.Fn archive_write_set_format_ustar "struct archive *"
-.Ft int
-.Fn archive_write_set_format_options "struct archive *" "const char *"
-.Ft int
-.Fn archive_write_set_compressor_options "struct archive *" "const char *"
-.Ft int
-.Fn archive_write_set_options "struct archive *" "const char *"
-.Ft int
-.Fo archive_write_open
-.Fa "struct archive *"
-.Fa "void *client_data"
-.Fa "archive_open_callback *"
-.Fa "archive_write_callback *"
-.Fa "archive_close_callback *"
-.Fc
-.Ft int
-.Fn archive_write_open_fd "struct archive *" "int fd"
-.Ft int
-.Fn archive_write_open_FILE "struct archive *" "FILE *file"
-.Ft int
-.Fn archive_write_open_filename "struct archive *" "const char *filename"
-.Ft int
-.Fo archive_write_open_memory
-.Fa "struct archive *"
-.Fa "void *buffer"
-.Fa "size_t bufferSize"
-.Fa "size_t *outUsed"
-.Fc
-.Ft int
-.Fn archive_write_header "struct archive *" "struct archive_entry *"
-.Ft ssize_t
-.Fn archive_write_data "struct archive *" "const void *" "size_t"
-.Ft int
-.Fn archive_write_finish_entry "struct archive *"
-.Ft int
-.Fn archive_write_close "struct archive *"
-.Ft int
-.Fn archive_write_free "struct archive *"
 .Sh DESCRIPTION
 These functions provide a complete API for creating streaming
 archive files.
@@ -133,316 +39,83 @@
 .Tn struct archive
 object, set any desired options, initialize the archive, append entries, then
 close the archive and release all resources.
-The following summary describes the functions in approximately
-the order they are ordinarily used:
-.Bl -tag -width indent
-.It Fn archive_write_new
-Allocates and initializes a
+.\"
+.Ss Create archive object
+See
+.Xr archive_write_new 3 .
+.Pp
+To write an archive, you must first obtain an initialized
 .Tn struct archive
-object suitable for writing a tar archive.
-.It Fn archive_write_set_bytes_per_block
-Sets the block size used for writing the archive data.
-Every call to the write callback function, except possibly the last one, will
-use this value for the length.
-The third parameter is a boolean that specifies whether or not the final block
-written will be padded to the full block size.
-If it is zero, the last block will not be padded.
-If it is non-zero, padding will be added both before and after compression.
-The default is to use a block size of 10240 bytes and to pad the last block.
-Note that a block size of zero will suppress internal blocking
-and cause writes to be sent directly to the write callback as they occur.
-.It Fn archive_write_get_bytes_per_block
-Retrieve the block size to be used for writing.
-A value of -1 here indicates that the library should use default values.
-A value of zero indicates that internal blocking is suppressed.
-.It Fn archive_write_set_bytes_in_last_block
-Sets the block size used for writing the last block.
-If this value is zero, the last block will be padded to the same size
-as the other blocks.
-Otherwise, the final block will be padded to a multiple of this size.
-In particular, setting it to 1 will cause the final block to not be padded.
-For compressed output, any padding generated by this option
-is applied only after the compression.
-The uncompressed data is always unpadded.
-The default is to pad the last block to the full block size (note that
-.Fn archive_write_open_filename
-will set this based on the file type).
-Unlike the other
-.Dq set
-functions, this function can be called after the archive is opened.
-.It Fn archive_write_get_bytes_in_last_block
-Retrieve the currently-set value for last block size.
-A value of -1 here indicates that the library should use default values.
-.It Xo
-.Fn archive_write_set_format_cpio ,
-.Fn archive_write_set_format_pax ,
-.Fn archive_write_set_format_pax_restricted ,
-.Fn archive_write_set_format_shar ,
-.Fn archive_write_set_format_shar_binary ,
-.Fn archive_write_set_format_ustar
-.Xc
-Sets the format that will be used for the archive.
-The library can write
-POSIX octet-oriented cpio format archives,
-POSIX-standard
-.Dq pax interchange
-format archives,
-traditional
-.Dq shar
-archives,
-enhanced
-.Dq binary
-shar archives that store a variety of file attributes and handle binary files,
+object from
+.Fn archive_write_new .
+.\"
+.Ss Enable filters and formats, configure block size and padding
+See
+.Xr archive_write_filter 3 ,
+.Xr archive_write_format 3
 and
-POSIX-standard
-.Dq ustar
-archives.
-The pax interchange format is a backwards-compatible tar format that
-adds key/value attributes to each entry and supports arbitrary
-filenames, linknames, uids, sizes, etc.
-.Dq Restricted pax interchange format
-is the library default; this is the same as pax format, but suppresses
-the pax extended header for most normal files.
-In most cases, this will result in ordinary ustar archives.
-.It Xo
-.Fn archive_write_set_compression_bzip2 ,
-.Fn archive_write_set_compression_compress ,
-.Fn archive_write_set_compression_gzip ,
-.Fn archive_write_set_compression_none
-.Xc
-The resulting archive will be compressed as specified.
-Note that the compressed output is always properly blocked.
-.It Fn archive_write_set_compression_program
-The archive will be fed into the specified compression program.
-The output of that program is blocked and written to the client
-write callbacks.
-.It Xo
-.Fn archive_write_set_compressor_options ,
-.Fn archive_write_set_format_options ,
-.Fn archive_write_set_options
-.Xc
-Specifies options that will be passed to the currently-enabled
-compressor and/or format writer.
-The argument is a comma-separated list of individual options.
-Individual options have one of the following forms:
-.Bl -tag -compact -width indent
-.It Ar option=value
-The option/value pair will be provided to every module.
-Modules that do not accept an option with this name will ignore it.
-.It Ar option
-The option will be provided to every module with a value of
-.Dq 1 .
-.It Ar !option
-The option will be provided to every module with a NULL value.
-.It Ar module:option=value , Ar module:option , Ar module:!option
-As above, but the corresponding option and value will be provided
-only to modules whose name matches
-.Ar module .
-.El
-The return value will be
-.Cm ARCHIVE_OK
-if any module accepts the option, or
-.Cm ARCHIVE_WARN
-if no module accepted the option, or
-.Cm ARCHIVE_FATAL
-if there was a fatal error while attempting to process the option.
+.Xr archive_write_blocksize 3 .
 .Pp
-The currently supported options are:
-.Bl -tag -compact -width indent
-.It Compressor gzip
-.Bl -tag -compact -width indent
-.It Cm compression-level
-The value is interpreted as a decimal integer specifying the
-gzip compression level.
-.El
-.It Compressor xz
-.Bl -tag -compact -width indent
-.It Cm compression-level
-The value is interpreted as a decimal integer specifying the
-compression level.
-.El
-.It Format mtree
-.Bl -tag -compact -width indent
-.It Cm cksum , Cm device , Cm flags , Cm gid , Cm gname , Cm indent , Cm link , Cm md5 , Cm mode , Cm nlink , Cm rmd160 , Cm sha1 , Cm sha256 , Cm sha384 , Cm sha512 , Cm size , Cm time , Cm uid , Cm uname
-Enable a particular keyword in the mtree output.
-Prefix with an exclamation mark to disable the corresponding keyword.
-The default is equivalent to
-.Dq device, flags, gid, gname, link, mode, nlink, size, time, type, uid, uname .
-.It Cm all
-Enables all of the above keywords.
-.It Cm use-set
-Enables generation of
-.Cm /set
-lines that specify default values for the following files and/or directories.
-.It Cm indent
-XXX needs explanation XXX
-.El
-.El
-.It Fn archive_write_open
-Freeze the settings, open the archive, and prepare for writing entries.
-This is the most generic form of this function, which accepts
-pointers to three callback functions which will be invoked by
-the compression layer to write the constructed archive.
-.It Fn archive_write_open_fd
-A convenience form of
+You can then modify this object for the desired operations with the
+various
+.Fn archive_write_set_XXX
+functions.
+In particular, you will need to invoke appropriate
+.Fn archive_write_add_XXX
+and
+.Fn archive_write_set_XXX
+functions to enable the corresponding compression and format
+support.
+.\"
+.Ss Set options
+See
+.Xr archive_read_set_options 3 .
+.\"
+.Ss Open archive
+See
+.Xr archive_write_open 3 .
+.Pp
+Once you have prepared the
+.Tn struct archive
+object, you call
 .Fn archive_write_open
-that accepts a file descriptor.
-The
-.Fn archive_write_open_fd
-function is safe for use with tape drives or other
-block-oriented devices.
-.It Fn archive_write_open_FILE
-A convenience form of
-.Fn archive_write_open
-that accepts a
+to actually open the archive and prepare it for writing.
+There are several variants of this function;
+the most basic expects you to provide pointers to several
+functions that can provide blocks of bytes from the archive.
+There are convenience forms that allow you to
+specify a filename, file descriptor,
 .Ft "FILE *"
-pointer.
-Note that
-.Fn archive_write_open_FILE
-is not safe for writing to tape drives or other devices
-that require correct blocking.
-.It Fn archive_write_open_file
-A deprecated synonym for
-.Fn archive_write_open_filename .
-.It Fn archive_write_open_filename
-A convenience form of
-.Fn archive_write_open
-that accepts a filename.
-A NULL argument indicates that the output should be written to standard output;
-an argument of
-.Dq -
-will open a file with that name.
-If you have not invoked
-.Fn archive_write_set_bytes_in_last_block ,
-then
-.Fn archive_write_open_filename
-will adjust the last-block padding depending on the file:
-it will enable padding when writing to standard output or
-to a character or block device node, it will disable padding otherwise.
-You can override this by manually invoking
-.Fn archive_write_set_bytes_in_last_block
-before calling
-.Fn archive_write_open .
-The
-.Fn archive_write_open_filename
-function is safe for use with tape drives or other
-block-oriented devices.
-.It Fn archive_write_open_memory
-A convenience form of
-.Fn archive_write_open
-that accepts a pointer to a block of memory that will receive
-the archive.
-The final
-.Ft "size_t *"
-argument points to a variable that will be updated
-after each write to reflect how much of the buffer
-is currently in use.
-You should be careful to ensure that this variable
-remains allocated until after the archive is
-closed.
-.It Fn archive_write_header
-Build and write a header using the data in the provided
+object, or a block of memory from which to write the archive data.
+.\"
+.Ss Produce archive
+See
+.Xr archive_write_header 3
+and
+.Xr archive_write_data 3 .
+.Pp
+Individual archive entries are written in a three-step
+process:
+You first initialize a
 .Tn struct archive_entry
-structure.
+structure with information about the new entry.
+At a minimum, you should set the pathname of the
+entry and provide a
+.Va struct stat
+with a valid
+.Va st_mode
+field, which specifies the type of object and
+.Va st_size
+field, which specifies the size of the data portion of the object.
+.\"
+.Ss Release resources
 See
-.Xr archive_entry 3
-for information on creating and populating
-.Tn struct archive_entry
-objects.
-.It Fn archive_write_data
-Write data corresponding to the header just written.
-Returns number of bytes written or -1 on error.
-.It Fn archive_write_finish_entry
-Close out the entry just written.
-In particular, this writes out the final padding required by some formats.
-Ordinarily, clients never need to call this, as it
-is called automatically by
-.Fn archive_write_next_header
-and
-.Fn archive_write_close
-as needed.
-.It Fn archive_write_close
-Complete the archive and invoke the close callback.
-.It Fn archive_write_free
-Invokes
-.Fn archive_write_close
-if necessary, then releases all resources.
-If you need detailed information about
-.Fn archive_write_close
-failures, you should be careful to call it separately, as
-you cannot obtain error information after
+.Xr archive_write_free 3 .
+.Pp
+After all entries have been written, use the
 .Fn archive_write_free
-returns.
-.El
-More information about the
-.Va struct archive
-object and the overall design of the library can be found in the
-.Xr libarchive 3
-overview.
-.Sh IMPLEMENTATION
-Compression support is built-in to libarchive, which uses zlib and bzlib
-to handle gzip and bzip2 compression, respectively.
-.Sh CLIENT CALLBACKS
-To use this library, you will need to define and register
-callback functions that will be invoked to write data to the
-resulting archive.
-These functions are registered by calling
-.Fn archive_write_open :
-.Bl -item -offset indent
-.It
-.Ft typedef int
-.Fn archive_open_callback "struct archive *" "void *client_data"
-.El
-.Pp
-The open callback is invoked by
-.Fn archive_write_open .
-It should return
-.Cm ARCHIVE_OK
-if the underlying file or data source is successfully
-opened.
-If the open fails, it should call
-.Fn archive_set_error
-to register an error code and message and return
-.Cm ARCHIVE_FATAL .
-.Bl -item -offset indent
-.It
-.Ft typedef ssize_t
-.Fo archive_write_callback
-.Fa "struct archive *"
-.Fa "void *client_data"
-.Fa "const void *buffer"
-.Fa "size_t length"
-.Fc
-.El
-.Pp
-The write callback is invoked whenever the library
-needs to write raw bytes to the archive.
-For correct blocking, each call to the write callback function
-should translate into a single
-.Xr write 2
-system call.
-This is especially critical when writing archives to tape drives.
-On success, the write callback should return the
-number of bytes actually written.
-On error, the callback should invoke
-.Fn archive_set_error
-to register an error code and message and return -1.
-.Bl -item -offset indent
-.It
-.Ft typedef int
-.Fn archive_close_callback "struct archive *" "void *client_data"
-.El
-.Pp
-The close callback is invoked by archive_close when
-the archive processing is complete.
-The callback should return
-.Cm ARCHIVE_OK
-on success.
-On failure, the callback should invoke
-.Fn archive_set_error
-to register an error code and message and
-return
-.Cm ARCHIVE_FATAL.
+function to release all resources.
+.\"
 .Sh EXAMPLE
 The following sketch illustrates basic usage of the library.
 In this example,
@@ -464,8 +137,8 @@
 #include <unistd.h>
 
 struct mydata {
-	const char *name;
-	int fd;
+  const char *name;
+  int fd;
 };
 
 int
@@ -511,7 +184,7 @@
 
   a = archive_write_new();
   mydata->name = outname;
-  archive_write_set_compression_gzip(a);
+  archive_write_add_filter_gzip(a);
   archive_write_set_format_ustar(a);
   archive_write_open(a, mydata, myopen, mywrite, myclose);
   while (*filename) {
@@ -520,11 +193,13 @@
     archive_entry_copy_stat(entry, &st);
     archive_entry_set_pathname(entry, *filename);
     archive_write_header(a, entry);
-    fd = open(*filename, O_RDONLY);
-    len = read(fd, buff, sizeof(buff));
-    while ( len > 0 ) {
-	archive_write_data(a, buff, len);
-	len = read(fd, buff, sizeof(buff));
+    if ((fd = open(*filename, O_RDONLY)) != -1) {
+      len = read(fd, buff, sizeof(buff));
+      while ( len > 0 ) {
+        archive_write_data(a, buff, len);
+        len = read(fd, buff, sizeof(buff));
+      }
+      close(fd);
     }
     archive_entry_free(entry);
     filename++;
@@ -534,62 +209,19 @@
 
 int main(int argc, const char **argv)
 {
-	const char *outname;
-	argv++;
-	outname = argv++;
-	write_archive(outname, argv);
-	return 0;
+  const char *outname;
+  argv++;
+  outname = argv++;
+  write_archive(outname, argv);
+  return 0;
 }
 .Ed
-.Sh RETURN VALUES
-Most functions return
-.Cm ARCHIVE_OK
-(zero) on success, or one of several non-zero
-error codes for errors.
-Specific error codes include:
-.Cm ARCHIVE_RETRY
-for operations that might succeed if retried,
-.Cm ARCHIVE_WARN
-for unusual conditions that do not prevent further operations, and
-.Cm ARCHIVE_FATAL
-for serious errors that make remaining operations impossible.
-The
-.Fn archive_errno
-and
-.Fn archive_error_string
-functions can be used to retrieve an appropriate error code and a
-textual error message.
-.Pp
-.Fn archive_write_new
-returns a pointer to a newly-allocated
-.Tn struct archive
-object.
-.Pp
-.Fn archive_write_data
-returns a count of the number of bytes actually written.
-On error, -1 is returned and the
-.Fn archive_errno
-and
-.Fn archive_error_string
-functions will return appropriate values.
-Note that if the client-provided write callback function
-returns a non-zero value, that error will be propagated back to the caller
-through whatever API function resulted in that call, which
-may include
-.Fn archive_write_header ,
-.Fn archive_write_data ,
-.Fn archive_write_close ,
-or
-.Fn archive_write_free .
-The client callback can call
-.Fn archive_set_error
-to provide values that can then be retrieved by
-.Fn archive_errno
-and
-.Fn archive_error_string .
 .Sh SEE ALSO
 .Xr tar 1 ,
 .Xr libarchive 3 ,
+.Xr archive_write_set_options 3 ,
+.Xr cpio 5 ,
+.Xr mtree 5 ,
 .Xr tar 5
 .Sh HISTORY
 The
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write.c
--- a/head/contrib/libarchive/libarchive/archive_write.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2003-2010 Tim Kientzle
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,12 +24,12 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write.c 228773 2011-12-21 15:18:52Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write.c 232153 2012-02-25 10:58:02Z mm $");
 
 /*
  * This file contains the "essential" portions of the write API, that
  * is, stuff that will essentially always be used by any client that
- * actually needs to write a archive.  Optional pieces have been, as
+ * actually needs to write an archive.  Optional pieces have been, as
  * far as possible, separated out into separate files to reduce
  * needlessly bloating statically-linked clients.
  */
@@ -37,6 +37,9 @@
 #ifdef HAVE_SYS_WAIT_H
 #include <sys/wait.h>
 #endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
 #ifdef HAVE_LIMITS_H
 #include <limits.h>
 #endif
@@ -59,12 +62,23 @@
 
 static struct archive_vtable *archive_write_vtable(void);
 
+static int	_archive_filter_code(struct archive *, int);
+static const char *_archive_filter_name(struct archive *, int);
+static int64_t	_archive_filter_bytes(struct archive *, int);
+static int  _archive_write_filter_count(struct archive *);
 static int	_archive_write_close(struct archive *);
 static int	_archive_write_free(struct archive *);
 static int	_archive_write_header(struct archive *, struct archive_entry *);
 static int	_archive_write_finish_entry(struct archive *);
 static ssize_t	_archive_write_data(struct archive *, const void *, size_t);
 
+struct archive_none {
+	size_t buffer_size;
+	size_t avail;
+	char *buffer;
+	char *next;
+};
+
 static struct archive_vtable *
 archive_write_vtable(void)
 {
@@ -73,10 +87,15 @@
 
 	if (!inited) {
 		av.archive_close = _archive_write_close;
+		av.archive_filter_bytes = _archive_filter_bytes;
+		av.archive_filter_code = _archive_filter_code;
+		av.archive_filter_name = _archive_filter_name;
+		av.archive_filter_count = _archive_write_filter_count;
 		av.archive_free = _archive_write_free;
 		av.archive_write_header = _archive_write_header;
 		av.archive_write_finish_entry = _archive_write_finish_entry;
 		av.archive_write_data = _archive_write_data;
+		inited = 1;
 	}
 	return (&av);
 }
@@ -114,133 +133,17 @@
 	}
 	memset(nulls, 0, a->null_length);
 	a->nulls = nulls;
-	/*
-	 * Set default compression, but don't set a default format.
-	 * Were we to set a default format here, we would force every
-	 * client to link in support for that format, even if they didn't
-	 * ever use it.
-	 */
-	archive_write_set_compression_none(&a->archive);
 	return (&a->archive);
 }
 
 /*
- * Set write options for the format. Returns 0 if successful.
- */
-int
-archive_write_set_format_options(struct archive *_a, const char *s)
-{
-	struct archive_write *a = (struct archive_write *)_a;
-	char key[64], val[64];
-	int len, r, ret = ARCHIVE_OK;
-
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
-	    ARCHIVE_STATE_NEW, "archive_write_set_format_options");
-	archive_clear_error(&a->archive);
-
-	if (s == NULL || *s == '\0')
-		return (ARCHIVE_OK);
-	if (a->format_options == NULL)
-		/* This format does not support option. */
-		return (ARCHIVE_OK);
-
-	while ((len = __archive_parse_options(s, a->format_name,
-	    sizeof(key), key, sizeof(val), val)) > 0) {
-		if (val[0] == '\0')
-			r = a->format_options(a, key, NULL);
-		else
-			r = a->format_options(a, key, val);
-		if (r == ARCHIVE_FATAL)
-			return (r);
-		if (r < ARCHIVE_OK) { /* This key was not handled. */
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-			    "Unsupported option ``%s''", key);
-			ret = ARCHIVE_WARN;
-		}
-		s += len;
-	}
-	if (len < 0) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Malformed options string.");
-		return (ARCHIVE_WARN);
-	}
-	return (ret);
-}
-
-/*
- * Set write options for the compressor. Returns 0 if successful.
- */
-int
-archive_write_set_compressor_options(struct archive *_a, const char *s)
-{
-	struct archive_write *a = (struct archive_write *)_a;
-	char key[64], val[64];
-	int len, r;
-	int ret = ARCHIVE_OK;
-
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
-	    ARCHIVE_STATE_NEW, "archive_write_set_compressor_options");
-	archive_clear_error(&a->archive);
-
-	if (s == NULL || *s == '\0')
-		return (ARCHIVE_OK);
-	if (a->compressor.options == NULL) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Unsupported option ``%s''", s);
-		/* This compressor does not support option. */
-		return (ARCHIVE_WARN);
-	}
-
-	while ((len = __archive_parse_options(s, a->archive.compression_name,
-	    sizeof(key), key, sizeof(val), val)) > 0) {
-		if (val[0] == '\0')
-			r = a->compressor.options(a, key, NULL);
-		else
-			r = a->compressor.options(a, key, val);
-		if (r == ARCHIVE_FATAL)
-			return (r);
-		if (r < ARCHIVE_OK) {
-			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-			    "Unsupported option ``%s''", key);
-			ret = ARCHIVE_WARN;
-		}
-		s += len;
-	}
-	if (len < 0) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Illegal format options.");
-		return (ARCHIVE_WARN);
-	}
-	return (ret);
-}
-
-/*
- * Set write options for the format and the compressor. Returns 0 if successful.
- */
-int
-archive_write_set_options(struct archive *_a, const char *s)
-{
-	int r1, r2;
-
-	r1 = archive_write_set_format_options(_a, s);
-	if (r1 < ARCHIVE_WARN)
-		return (r1);
-	r2 = archive_write_set_compressor_options(_a, s);
-	if (r2 < ARCHIVE_WARN)
-		return (r2);
-	if (r1 == ARCHIVE_WARN && r2 == ARCHIVE_WARN)
-		return (ARCHIVE_WARN);
-	return (ARCHIVE_OK);
-}
-
-/*
  * Set the block size.  Returns 0 if successful.
  */
 int
 archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block)
 {
 	struct archive_write *a = (struct archive_write *)_a;
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block");
 	a->bytes_per_block = bytes_per_block;
 	return (ARCHIVE_OK);
@@ -253,7 +156,7 @@
 archive_write_get_bytes_per_block(struct archive *_a)
 {
 	struct archive_write *a = (struct archive_write *)_a;
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_ANY, "archive_write_get_bytes_per_block");
 	return (a->bytes_per_block);
 }
@@ -266,7 +169,7 @@
 archive_write_set_bytes_in_last_block(struct archive *_a, int bytes)
 {
 	struct archive_write *a = (struct archive_write *)_a;
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block");
 	a->bytes_in_last_block = bytes;
 	return (ARCHIVE_OK);
@@ -279,27 +182,264 @@
 archive_write_get_bytes_in_last_block(struct archive *_a)
 {
 	struct archive_write *a = (struct archive_write *)_a;
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_ANY, "archive_write_get_bytes_in_last_block");
 	return (a->bytes_in_last_block);
 }
 
-
 /*
  * dev/ino of a file to be rejected.  Used to prevent adding
  * an archive to itself recursively.
  */
 int
-archive_write_set_skip_file(struct archive *_a, dev_t d, ino_t i)
+archive_write_set_skip_file(struct archive *_a, int64_t d, int64_t i)
 {
 	struct archive_write *a = (struct archive_write *)_a;
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_ANY, "archive_write_set_skip_file");
+	a->skip_file_set = 1;
 	a->skip_file_dev = d;
 	a->skip_file_ino = i;
 	return (ARCHIVE_OK);
 }
 
+/*
+ * Allocate and return the next filter structure.
+ */
+struct archive_write_filter *
+__archive_write_allocate_filter(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *f;
+
+	f = calloc(1, sizeof(*f));
+	f->archive = _a;
+	if (a->filter_first == NULL)
+		a->filter_first = f;
+	else
+		a->filter_last->next_filter = f;
+	a->filter_last = f;
+	return f;
+}
+
+/*
+ * Write data to a particular filter.
+ */
+int
+__archive_write_filter(struct archive_write_filter *f,
+    const void *buff, size_t length)
+{
+	int r;
+	if (length == 0)
+		return(ARCHIVE_OK);
+	r = (f->write)(f, buff, length);
+	f->bytes_written += length;
+	return (r);
+}
+
+/*
+ * Open a filter.
+ */
+int
+__archive_write_open_filter(struct archive_write_filter *f)
+{
+	if (f->open == NULL)
+		return (ARCHIVE_OK);
+	return (f->open)(f);
+}
+
+/*
+ * Close a filter.
+ */
+int
+__archive_write_close_filter(struct archive_write_filter *f)
+{
+	if (f->close != NULL)
+		return (f->close)(f);
+	if (f->next_filter != NULL)
+		return (__archive_write_close_filter(f->next_filter));
+	return (ARCHIVE_OK);
+}
+
+int
+__archive_write_output(struct archive_write *a, const void *buff, size_t length)
+{
+	return (__archive_write_filter(a->filter_first, buff, length));
+}
+
+int
+__archive_write_nulls(struct archive_write *a, size_t length)
+{
+	if (length == 0)
+		return (ARCHIVE_OK);
+
+	while (length > 0) {
+		size_t to_write = length < a->null_length ? length : a->null_length;
+		int r = __archive_write_output(a, a->nulls, to_write);
+		if (r < ARCHIVE_OK)
+			return (r);
+		length -= to_write;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_client_open(struct archive_write_filter *f)
+{
+	struct archive_write *a = (struct archive_write *)f->archive;
+	struct archive_none *state;
+	void *buffer;
+	size_t buffer_size;
+
+	f->bytes_per_block = archive_write_get_bytes_per_block(f->archive);
+	f->bytes_in_last_block =
+	    archive_write_get_bytes_in_last_block(f->archive);
+	buffer_size = f->bytes_per_block;
+
+	state = (struct archive_none *)calloc(1, sizeof(*state));
+	buffer = (char *)malloc(buffer_size);
+	if (state == NULL || buffer == NULL) {
+		free(state);
+		free(buffer);
+		archive_set_error(f->archive, ENOMEM,
+		    "Can't allocate data for output buffering");
+		return (ARCHIVE_FATAL);
+	}
+
+	state->buffer_size = buffer_size;
+	state->buffer = buffer;
+	state->next = state->buffer;
+	state->avail = state->buffer_size;
+	f->data = state;
+
+	if (a->client_opener == NULL)
+		return (ARCHIVE_OK);
+	return (a->client_opener(f->archive, a->client_data));
+}
+
+static int
+archive_write_client_write(struct archive_write_filter *f,
+    const void *_buff, size_t length)
+{
+	struct archive_write *a = (struct archive_write *)f->archive;
+        struct archive_none *state = (struct archive_none *)f->data;
+	const char *buff = (const char *)_buff;
+	ssize_t remaining, to_copy;
+	ssize_t bytes_written;
+
+	remaining = length;
+
+	/*
+	 * If there is no buffer for blocking, just pass the data
+	 * straight through to the client write callback.  In
+	 * particular, this supports "no write delay" operation for
+	 * special applications.  Just set the block size to zero.
+	 */
+	if (state->buffer_size == 0) {
+		while (remaining > 0) {
+			bytes_written = (a->client_writer)(&a->archive,
+			    a->client_data, buff, remaining);
+			if (bytes_written <= 0)
+				return (ARCHIVE_FATAL);
+			remaining -= bytes_written;
+			buff += bytes_written;
+		}
+		return (ARCHIVE_OK);
+	}
+
+	/* If the copy buffer isn't empty, try to fill it. */
+	if (state->avail < state->buffer_size) {
+		/* If buffer is not empty... */
+		/* ... copy data into buffer ... */
+		to_copy = ((size_t)remaining > state->avail) ?
+			state->avail : (size_t)remaining;
+		memcpy(state->next, buff, to_copy);
+		state->next += to_copy;
+		state->avail -= to_copy;
+		buff += to_copy;
+		remaining -= to_copy;
+		/* ... if it's full, write it out. */
+		if (state->avail == 0) {
+			char *p = state->buffer;
+			size_t to_write = state->buffer_size;
+			while (to_write > 0) {
+				bytes_written = (a->client_writer)(&a->archive,
+				    a->client_data, p, to_write);
+				if (bytes_written <= 0)
+					return (ARCHIVE_FATAL);
+				if ((size_t)bytes_written > to_write) {
+					archive_set_error(&(a->archive),
+					    -1, "write overrun");
+					return (ARCHIVE_FATAL);
+				}
+				p += bytes_written;
+				to_write -= bytes_written;
+			}
+			state->next = state->buffer;
+			state->avail = state->buffer_size;
+		}
+	}
+
+	while ((size_t)remaining > state->buffer_size) {
+		/* Write out full blocks directly to client. */
+		bytes_written = (a->client_writer)(&a->archive,
+		    a->client_data, buff, state->buffer_size);
+		if (bytes_written <= 0)
+			return (ARCHIVE_FATAL);
+		buff += bytes_written;
+		remaining -= bytes_written;
+	}
+
+	if (remaining > 0) {
+		/* Copy last bit into copy buffer. */
+		memcpy(state->next, buff, remaining);
+		state->next += remaining;
+		state->avail -= remaining;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_client_close(struct archive_write_filter *f)
+{
+	struct archive_write *a = (struct archive_write *)f->archive;
+	struct archive_none *state = (struct archive_none *)f->data;
+	ssize_t block_length;
+	ssize_t target_block_length;
+	ssize_t bytes_written;
+	int ret = ARCHIVE_OK;
+
+	/* If there's pending data, pad and write the last block */
+	if (state->next != state->buffer) {
+		block_length = state->buffer_size - state->avail;
+
+		/* Tricky calculation to determine size of last block */
+		if (a->bytes_in_last_block <= 0)
+			/* Default or Zero: pad to full block */
+			target_block_length = a->bytes_per_block;
+		else
+			/* Round to next multiple of bytes_in_last_block. */
+			target_block_length = a->bytes_in_last_block *
+			    ( (block_length + a->bytes_in_last_block - 1) /
+			        a->bytes_in_last_block);
+		if (target_block_length > a->bytes_per_block)
+			target_block_length = a->bytes_per_block;
+		if (block_length < target_block_length) {
+			memset(state->next, 0,
+			    target_block_length - block_length);
+			block_length = target_block_length;
+		}
+		bytes_written = (a->client_writer)(&a->archive,
+		    a->client_data, state->buffer, block_length);
+		ret = bytes_written <= 0 ? ARCHIVE_FATAL : ARCHIVE_OK;
+	}
+	if (a->client_closer)
+		(*a->client_closer)(&a->archive, a->client_data);
+	free(state->buffer);
+	free(state);
+	a->client_data = NULL;
+	return (ret);
+}
 
 /*
  * Open the archive using the current settings.
@@ -310,29 +450,37 @@
     archive_close_callback *closer)
 {
 	struct archive_write *a = (struct archive_write *)_a;
-	int ret;
+	struct archive_write_filter *client_filter;
+	int ret, r1;
 
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_NEW, "archive_write_open");
 	archive_clear_error(&a->archive);
-	a->archive.state = ARCHIVE_STATE_HEADER;
-	a->client_data = client_data;
+
 	a->client_writer = writer;
 	a->client_opener = opener;
 	a->client_closer = closer;
-	ret = (a->compressor.init)(a);
-	if (a->format_init && ret == ARCHIVE_OK)
+	a->client_data = client_data;
+
+	client_filter = __archive_write_allocate_filter(_a);
+	client_filter->open = archive_write_client_open;
+	client_filter->write = archive_write_client_write;
+	client_filter->close = archive_write_client_close;
+
+	ret = __archive_write_open_filter(a->filter_first);
+	if (ret < ARCHIVE_WARN) {
+		r1 = __archive_write_close_filter(a->filter_first);
+		return (r1 < ret ? r1 : ret);
+	}
+
+	a->archive.state = ARCHIVE_STATE_HEADER;
+	if (a->format_init)
 		ret = (a->format_init)(a);
 	return (ret);
 }
 
-
 /*
  * Close out the archive.
- *
- * Be careful: user might just call write_new and then write_finish.
- * Don't assume we actually wrote anything or performed any non-trivial
- * initialization.
  */
 static int
 _archive_write_close(struct archive *_a)
@@ -340,63 +488,105 @@
 	struct archive_write *a = (struct archive_write *)_a;
 	int r = ARCHIVE_OK, r1 = ARCHIVE_OK;
 
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
-	    ARCHIVE_STATE_ANY, "archive_write_close");
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL,
+	    "archive_write_close");
+	if (a->archive.state == ARCHIVE_STATE_NEW
+	    || a->archive.state == ARCHIVE_STATE_CLOSED)
+		return (ARCHIVE_OK); /* Okay to close() when not open. */
+
+	archive_clear_error(&a->archive);
 
 	/* Finish the last entry. */
-	if (a->archive.state & ARCHIVE_STATE_DATA)
+	if (a->archive.state == ARCHIVE_STATE_DATA)
 		r = ((a->format_finish_entry)(a));
 
 	/* Finish off the archive. */
-	if (a->format_finish != NULL) {
-		r1 = (a->format_finish)(a);
-		if (r1 < r)
-			r = r1;
-	}
-
-	/* Release format resources. */
-	if (a->format_destroy != NULL) {
-		r1 = (a->format_destroy)(a);
+	/* TODO: have format closers invoke compression close. */
+	if (a->format_close != NULL) {
+		r1 = (a->format_close)(a);
 		if (r1 < r)
 			r = r1;
 	}
 
 	/* Finish the compression and close the stream. */
-	if (a->compressor.finish != NULL) {
-		r1 = (a->compressor.finish)(a);
-		if (r1 < r)
-			r = r1;
+	r1 = __archive_write_close_filter(a->filter_first);
+	if (r1 < r)
+		r = r1;
+
+	if (a->archive.state != ARCHIVE_STATE_FATAL)
+		a->archive.state = ARCHIVE_STATE_CLOSED;
+	return (r);
+}
+
+static int
+_archive_write_filter_count(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *p = a->filter_first;
+	int count = 0;
+	while(p) {
+		count++;
+		p = p->next_filter;
 	}
+	return count;
+}
 
-	/* Close out the client stream. */
-	if (a->client_closer != NULL) {
-		r1 = (a->client_closer)(&a->archive, a->client_data);
-		if (r1 < r)
-			r = r1;
+void
+__archive_write_filters_free(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	int r = ARCHIVE_OK, r1;
+
+	while (a->filter_first != NULL) {
+		struct archive_write_filter *next
+		    = a->filter_first->next_filter;
+		if (a->filter_first->free != NULL) {
+			r1 = (*a->filter_first->free)(a->filter_first);
+			if (r > r1)
+				r = r1;
+		}
+		free(a->filter_first);
+		a->filter_first = next;
 	}
-
-	a->archive.state = ARCHIVE_STATE_CLOSED;
-	return (r);
+	a->filter_last = NULL;
 }
 
 /*
  * Destroy the archive structure.
+ *
+ * Be careful: user might just call write_new and then write_free.
+ * Don't assume we actually wrote anything or performed any non-trivial
+ * initialization.
  */
 static int
 _archive_write_free(struct archive *_a)
 {
 	struct archive_write *a = (struct archive_write *)_a;
-	int r = ARCHIVE_OK;
+	int r = ARCHIVE_OK, r1;
 
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
-	    ARCHIVE_STATE_ANY, "archive_write_free");
-	if (a->archive.state != ARCHIVE_STATE_CLOSED)
+	if (_a == NULL)
+		return (ARCHIVE_OK);
+	/* It is okay to call free() in state FATAL. */
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_free");
+	if (a->archive.state != ARCHIVE_STATE_FATAL)
 		r = archive_write_close(&a->archive);
 
+	/* Release format resources. */
+	if (a->format_free != NULL) {
+		r1 = (a->format_free)(a);
+		if (r1 < r)
+			r = r1;
+	}
+
+	__archive_write_filters_free(_a);
+
 	/* Release various dynamic buffers. */
 	free((void *)(uintptr_t)(const void *)a->nulls);
 	archive_string_free(&a->archive.error_string);
 	a->archive.magic = 0;
+	__archive_clean(&a->archive);
 	free(a);
 	return (r);
 }
@@ -410,18 +600,30 @@
 	struct archive_write *a = (struct archive_write *)_a;
 	int ret, r2;
 
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_DATA | ARCHIVE_STATE_HEADER, "archive_write_header");
 	archive_clear_error(&a->archive);
 
+	if (a->format_write_header == NULL) {
+		archive_set_error(&(a->archive), -1,
+		    "Format must be set before you can write to an archive.");
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+
 	/* In particular, "retry" and "fatal" get returned immediately. */
 	ret = archive_write_finish_entry(&a->archive);
+	if (ret == ARCHIVE_FATAL) {
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
 	if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN)
 		return (ret);
 
-	if (a->skip_file_dev != 0 &&
+	if (a->skip_file_set &&
+	    archive_entry_dev_is_set(entry) &&
+	    archive_entry_ino_is_set(entry) &&
 	    archive_entry_dev(entry) == a->skip_file_dev &&
-	    a->skip_file_ino != 0 &&
 	    archive_entry_ino64(entry) == a->skip_file_ino) {
 		archive_set_error(&a->archive, 0,
 		    "Can't add archive to itself");
@@ -430,6 +632,10 @@
 
 	/* Format and write header. */
 	r2 = ((a->format_write_header)(a, entry));
+	if (r2 == ARCHIVE_FATAL) {
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
 	if (r2 < ret)
 		ret = r2;
 
@@ -443,7 +649,7 @@
 	struct archive_write *a = (struct archive_write *)_a;
 	int ret = ARCHIVE_OK;
 
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
 	    "archive_write_finish_entry");
 	if (a->archive.state & ARCHIVE_STATE_DATA)
@@ -459,8 +665,45 @@
 _archive_write_data(struct archive *_a, const void *buff, size_t s)
 {
 	struct archive_write *a = (struct archive_write *)_a;
-	__archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
 	    ARCHIVE_STATE_DATA, "archive_write_data");
 	archive_clear_error(&a->archive);
 	return ((a->format_write_data)(a, buff, s));
 }
+
+static struct archive_write_filter *
+filter_lookup(struct archive *_a, int n)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *f = a->filter_first;
+	if (n == -1)
+		return a->filter_last;
+	if (n < 0)
+		return NULL;
+	while (n > 0 && f != NULL) {
+		f = f->next_filter;
+		--n;
+	}
+	return f;
+}
+
+static int
+_archive_filter_code(struct archive *_a, int n)
+{
+	struct archive_write_filter *f = filter_lookup(_a, n);
+	return f == NULL ? -1 : f->code;
+}
+
+static const char *
+_archive_filter_name(struct archive *_a, int n)
+{
+	struct archive_write_filter *f = filter_lookup(_a, n);
+	return f == NULL ? NULL : f->name;
+}
+
+static int64_t
+_archive_filter_bytes(struct archive *_a, int n)
+{
+	struct archive_write_filter *f = filter_lookup(_a, n);
+	return f == NULL ? -1 : f->bytes_written;
+}
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write_disk.3
--- a/head/contrib/libarchive/libarchive/archive_write_disk.3	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write_disk.3	Fri Mar 02 16:54:40 2012 +0200
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: head/contrib/libarchive/libarchive/archive_write_disk.3 228773 2011-12-21 15:18:52Z mm $
+.\" $FreeBSD: head/contrib/libarchive/libarchive/archive_write_disk.3 232153 2012-02-25 10:58:02Z mm $
 .\"
 .Dd August 5, 2008
 .Dt ARCHIVE_WRITE_DISK 3
@@ -36,8 +36,10 @@
 .Nm archive_write_disk_set_user_lookup ,
 .Nm archive_write_header ,
 .Nm archive_write_data ,
+.Nm archive_write_data_block ,
 .Nm archive_write_finish_entry ,
 .Nm archive_write_close ,
+.Nm archive_write_finish
 .Nm archive_write_free
 .Nd functions for creating objects on disk
 .Sh SYNOPSIS
@@ -68,11 +70,15 @@
 .Fn archive_write_header "struct archive *" "struct archive_entry *"
 .Ft ssize_t
 .Fn archive_write_data "struct archive *" "const void *" "size_t"
+.Ft ssize_t
+.Fn archive_write_data_block "struct archive *" "const void *" "size_t size" "int64_t offset"
 .Ft int
 .Fn archive_write_finish_entry "struct archive *"
 .Ft int
 .Fn archive_write_close "struct archive *"
 .Ft int
+.Fn archive_write_finish "struct archive *"
+.Ft int
 .Fn archive_write_free "struct archive *"
 .Sh DESCRIPTION
 These functions provide a complete API for creating objects on
@@ -221,6 +227,20 @@
 .It Fn archive_write_data
 Write data corresponding to the header just written.
 Returns number of bytes written or -1 on error.
+.It Fn archive_write_data_block
+Write data corresponding to the header just written.
+This is like
+.Fn archive_write_data
+except that it performs a seek on the file being
+written to the specified offset before writing the data.
+This is useful when restoring sparse files from archive
+formats that support sparse files.
+Returns number of bytes written or -1 on error.
+(Note: This is currently not supported for
+.Tn archive_write
+handles, only for
+.Tn archive_write_disk
+handles.)
 .It Fn archive_write_finish_entry
 Close out the entry just written.
 Ordinarily, clients never need to call this, as it
@@ -229,6 +249,9 @@
 and
 .Fn archive_write_close
 as needed.
+However, some file attributes are written to disk only
+after the file is closed, so this can be necessary
+if you need to work with the file on disk right away.
 .It Fn archive_write_close
 Set any attributes that could not be set during the initial restore.
 For example, directory timestamps are not restored initially because
@@ -239,6 +262,9 @@
 .Nm
 library maintains a list of all such deferred attributes and
 sets them when this function is invoked.
+.It Fn archive_write_finish
+This is a deprecated synonym for
+.Fn archive_write_free .
 .It Fn archive_write_free
 Invokes
 .Fn archive_write_close
@@ -263,12 +289,6 @@
 for unusual conditions that do not prevent further operations, and
 .Cm ARCHIVE_FATAL
 for serious errors that make remaining operations impossible.
-The
-.Fn archive_errno
-and
-.Fn archive_error_string
-functions can be used to retrieve an appropriate error code and a
-textual error message.
 .Pp
 .Fn archive_write_disk_new
 returns a pointer to a newly-allocated
@@ -276,12 +296,18 @@
 object.
 .Pp
 .Fn archive_write_data
-returns a count of the number of bytes actually written.
-On error, -1 is returned and the
+returns a count of the number of bytes actually written,
+or
+.Li -1
+on error.
+.\"
+.Sh ERRORS
+Detailed error codes and textual descriptions are available from the
 .Fn archive_errno
 and
 .Fn archive_error_string
-functions will return appropriate values.
+functions.
+.\"
 .Sh SEE ALSO
 .Xr archive_read 3 ,
 .Xr archive_write 3 ,
@@ -372,4 +398,4 @@
 There should be a corresponding
 .Nm archive_read_disk
 interface that walks a directory hierarchy and returns archive
-entry objects.
\ No newline at end of file
+entry objects.
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c
--- a/head/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -58,8 +58,8 @@
 
 static const size_t cache_size = 127;
 static unsigned int	hash(const char *);
-static gid_t	lookup_gid(void *, const char *uname, gid_t);
-static uid_t	lookup_uid(void *, const char *uname, uid_t);
+static int64_t	lookup_gid(void *, const char *uname, int64_t);
+static int64_t	lookup_uid(void *, const char *uname, int64_t);
 static void	cleanup(void *);
 
 /*
@@ -93,8 +93,8 @@
 	return (ARCHIVE_OK);
 }
 
-static gid_t
-lookup_gid(void *private_data, const char *gname, gid_t gid)
+static int64_t
+lookup_gid(void *private_data, const char *gname, int64_t gid)
 {
 	int h;
 	struct bucket *b;
@@ -163,8 +163,8 @@
 	return (gid);
 }
 
-static uid_t
-lookup_uid(void *private_data, const char *uname, uid_t uid)
+static int64_t
+lookup_uid(void *private_data, const char *uname, int64_t uid)
 {
 	int h;
 	struct bucket *b;
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write_open_filename.c
--- a/head/contrib/libarchive/libarchive/archive_write_open_filename.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write_open_filename.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_open_filename.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_open_filename.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -46,6 +46,7 @@
 #endif
 
 #include "archive.h"
+#include "archive_string.h"
 
 #ifndef O_BINARY
 #define O_BINARY 0
@@ -53,7 +54,11 @@
 
 struct write_file_data {
 	int		fd;
-	char		filename[1];
+	char		mbs_filename;
+	union {
+		char		m[1];
+		wchar_t		w[1];
+	} filename; /* Must be last! */
 };
 
 static int	file_close(struct archive *, void *);
@@ -79,12 +84,60 @@
 		archive_set_error(a, ENOMEM, "No memory");
 		return (ARCHIVE_FATAL);
 	}
-	strcpy(mine->filename, filename);
+	strcpy(mine->filename.m, filename);
+	mine->mbs_filename = 1;
 	mine->fd = -1;
 	return (archive_write_open(a, mine,
 		file_open, file_write, file_close));
 }
 
+int
+archive_write_open_filename_w(struct archive *a, const wchar_t *filename)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	struct write_file_data *mine;
+
+	if (filename == NULL || filename[0] == L'\0')
+		return (archive_write_open_fd(a, 1));
+
+	mine = malloc(sizeof(*mine) + wcslen(filename) * sizeof(wchar_t));
+	if (mine == NULL) {
+		archive_set_error(a, ENOMEM, "No memory");
+		return (ARCHIVE_FATAL);
+	}
+	wcscpy(mine->filename.w, filename);
+	mine->mbs_filename = 0;
+	mine->fd = -1;
+	return (archive_write_open(a, mine,
+		file_open, file_write, file_close));
+#else
+	/*
+	 * POSIX system does not support a wchar_t interface for
+	 * open() system call, so we have to translate a wchar_t
+	 * filename to multi-byte one and use it.
+	 */
+	struct archive_string fn;
+	int r;
+
+	if (filename == NULL || filename[0] == L'\0')
+		return (archive_write_open_fd(a, 1));
+
+	archive_string_init(&fn);
+	if (archive_string_append_from_wcs(&fn, filename,
+	    wcslen(filename)) != 0) {
+		archive_set_error(a, EINVAL,
+		    "Failed to convert a wide-character filename to"
+		    " a multi-byte filename");
+		archive_string_free(&fn);
+		return (ARCHIVE_FATAL);
+	}
+	r = archive_write_open_filename(a, fn.s);
+	archive_string_free(&fn);
+	return (r);
+#endif
+}
+
+
 static int
 file_open(struct archive *a, void *client_data)
 {
@@ -98,17 +151,46 @@
 	/*
 	 * Open the file.
 	 */
-	mine->fd = open(mine->filename, flags, 0666);
-	if (mine->fd < 0) {
-		archive_set_error(a, errno, "Failed to open '%s'",
-		    mine->filename);
+	if (mine->mbs_filename) {
+		mine->fd = open(mine->filename.m, flags, 0666);
+		if (mine->fd < 0) {
+			archive_set_error(a, errno, "Failed to open '%s'",
+			    mine->filename.m);
+			return (ARCHIVE_FATAL);
+		}
+
+		if (fstat(mine->fd, &st) != 0) {
+			archive_set_error(a, errno, "Couldn't stat '%s'",
+			    mine->filename.m);
+			return (ARCHIVE_FATAL);
+		}
+	} else {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		mine->fd = _wopen(mine->filename.w, flags, 0666);
+		if (mine->fd < 0 && errno == ENOENT) {
+			wchar_t *fullpath;
+			fullpath = __la_win_permissive_name_w(mine->filename.w);
+			if (fullpath != NULL) {
+				mine->fd = _wopen(fullpath, flags, 0666);
+				free(fullpath);
+			}
+		}
+		if (mine->fd < 0) {
+			archive_set_error(a, errno, "Failed to open '%S'",
+			    mine->filename.w);
+			return (ARCHIVE_FATAL);
+		}
+
+		if (fstat(mine->fd, &st) != 0) {
+			archive_set_error(a, errno, "Couldn't stat '%S'",
+			    mine->filename.w);
+			return (ARCHIVE_FATAL);
+		}
+#else
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Unexpedted operation in archive_write_open_filename");
 		return (ARCHIVE_FATAL);
-	}
-
-	if (fstat(mine->fd, &st) != 0) {
-               archive_set_error(a, errno, "Couldn't stat '%s'",
-                   mine->filename);
-               return (ARCHIVE_FATAL);
+#endif
 	}
 
 	/*
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write_open_memory.c
--- a/head/contrib/libarchive/libarchive/archive_write_open_memory.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write_open_memory.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_open_memory.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_open_memory.c 232153 2012-02-25 10:58:02Z mm $");
 
 #include <errno.h>
 #include <stdlib.h>
@@ -32,18 +32,6 @@
 
 #include "archive.h"
 
-/*
- * This is a little tricky.  I used to allow the
- * compression handling layer to fork the compressor,
- * which means this write function gets invoked in
- * a separate process.  That would, of course, make it impossible
- * to actually use the data stored into memory here.
- * Fortunately, none of the compressors fork today and
- * I'm reluctant to use that route in the future but, if
- * forking compressors ever do reappear, this will have
- * to get a lot more complicated.
- */
-
 struct write_memory_data {
 	size_t	used;
 	size_t  size;
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write_private.h
--- a/head/contrib/libarchive/libarchive/archive_write_private.h	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write_private.h	Fri Mar 02 16:54:40 2012 +0200
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: head/contrib/libarchive/libarchive/archive_write_private.h 228763 2011-12-21 11:13:29Z mm $
+ * $FreeBSD: head/contrib/libarchive/libarchive/archive_write_private.h 232153 2012-02-25 10:58:02Z mm $
  */
 
 #ifndef __LIBARCHIVE_BUILD
@@ -36,10 +36,42 @@
 #include "archive_string.h"
 #include "archive_private.h"
 
+struct archive_write;
+
+struct archive_write_filter {
+	int64_t bytes_written;
+	struct archive *archive; /* Associated archive. */
+	struct archive_write_filter *next_filter; /* Who I write to. */
+	int	(*options)(struct archive_write_filter *,
+	    const char *key, const char *value);
+	int	(*open)(struct archive_write_filter *);
+	int	(*write)(struct archive_write_filter *, const void *, size_t);
+	int	(*close)(struct archive_write_filter *);
+	int	(*free)(struct archive_write_filter *);
+	void	 *data;
+	const char *name;
+	int	  code;
+	int	  bytes_per_block;
+	int	  bytes_in_last_block;
+};
+
+#if ARCHIVE_VERSION < 4000000
+void __archive_write_filters_free(struct archive *);
+#endif
+
+struct archive_write_filter *__archive_write_allocate_filter(struct archive *);
+
+int __archive_write_output(struct archive_write *, const void *, size_t);
+int __archive_write_nulls(struct archive_write *, size_t);
+int __archive_write_filter(struct archive_write_filter *, const void *, size_t);
+int __archive_write_open_filter(struct archive_write_filter *);
+int __archive_write_close_filter(struct archive_write_filter *);
+
 struct archive_write {
 	struct archive	archive;
 
 	/* Dev/ino of the archive being written. */
+	int		  skip_file_set;
 	dev_t		  skip_file_dev;
 	int64_t		  skip_file_ino;
 
@@ -63,29 +95,10 @@
 	int		  bytes_in_last_block;
 
 	/*
-	 * These control whether data within a gzip/bzip2 compressed
-	 * stream gets padded or not.  If pad_uncompressed is set,
-	 * the data will be padded to a full block before being
-	 * compressed.  The pad_uncompressed_byte determines the value
-	 * that will be used for padding.  Note that these have no
-	 * effect on compression "none."
+	 * First and last write filters in the pipeline.
 	 */
-	int		  pad_uncompressed;
-	int		  pad_uncompressed_byte; /* TODO: Support this. */
-
-	/*
-	 * On write, the client just invokes an archive_write_set function
-	 * which sets up the data here directly.
-	 */
-	struct {
-		void	 *data;
-		void	 *config;
-		int	(*init)(struct archive_write *);
-		int	(*options)(struct archive_write *,
-			    const char *key, const char *value);
-		int	(*finish)(struct archive_write *);
-		int	(*write)(struct archive_write *, const void *, size_t);
-	} compressor;
+	struct archive_write_filter *filter_first;
+	struct archive_write_filter *filter_last;
 
 	/*
 	 * Pointers to format-specific functions for writing.  They're
@@ -96,13 +109,13 @@
 	int	(*format_init)(struct archive_write *);
 	int	(*format_options)(struct archive_write *,
 		    const char *key, const char *value);
-	int	(*format_finish)(struct archive_write *);
-	int	(*format_destroy)(struct archive_write *);
 	int	(*format_finish_entry)(struct archive_write *);
 	int 	(*format_write_header)(struct archive_write *,
 		    struct archive_entry *);
 	ssize_t	(*format_write_data)(struct archive_write *,
 		    const void *buff, size_t);
+	int	(*format_close)(struct archive_write *);
+	int	(*format_free)(struct archive_write *);
 };
 
 /*
@@ -117,6 +130,7 @@
  */
 int
 __archive_write_format_header_ustar(struct archive_write *, char buff[512],
-    struct archive_entry *, int tartype, int strict);
+    struct archive_entry *, int tartype, int strict,
+    struct archive_string_conv *);
 
 #endif
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write_set_format.c
--- a/head/contrib/libarchive/libarchive/archive_write_set_format.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write_set_format.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -41,18 +41,22 @@
 static
 struct { int code; int (*setter)(struct archive *); } codes[] =
 {
+	{ ARCHIVE_FORMAT_7ZIP,		archive_write_set_format_7zip },
 	{ ARCHIVE_FORMAT_CPIO,		archive_write_set_format_cpio },
+	{ ARCHIVE_FORMAT_CPIO_POSIX,	archive_write_set_format_cpio },
 	{ ARCHIVE_FORMAT_CPIO_SVR4_NOCRC,	archive_write_set_format_cpio_newc },
-	{ ARCHIVE_FORMAT_CPIO_POSIX,	archive_write_set_format_cpio },
+	{ ARCHIVE_FORMAT_ISO9660,	archive_write_set_format_iso9660 },
 	{ ARCHIVE_FORMAT_MTREE,		archive_write_set_format_mtree },
 	{ ARCHIVE_FORMAT_SHAR,		archive_write_set_format_shar },
 	{ ARCHIVE_FORMAT_SHAR_BASE,	archive_write_set_format_shar },
 	{ ARCHIVE_FORMAT_SHAR_DUMP,	archive_write_set_format_shar_dump },
 	{ ARCHIVE_FORMAT_TAR,	archive_write_set_format_pax_restricted },
+	{ ARCHIVE_FORMAT_TAR_GNUTAR,	archive_write_set_format_gnutar },
 	{ ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, archive_write_set_format_pax },
 	{ ARCHIVE_FORMAT_TAR_PAX_RESTRICTED,
 				archive_write_set_format_pax_restricted },
 	{ ARCHIVE_FORMAT_TAR_USTAR,	archive_write_set_format_ustar },
+	{ ARCHIVE_FORMAT_XAR,		archive_write_set_format_xar },
 	{ ARCHIVE_FORMAT_ZIP,	archive_write_set_format_zip },
 	{ 0,		NULL }
 };
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write_set_format_ar.c
--- a/head/contrib/libarchive/libarchive/archive_write_set_format_ar.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write_set_format_ar.c	Fri Mar 02 16:54:40 2012 +0200
@@ -26,7 +26,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format_ar.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format_ar.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -48,6 +48,7 @@
 	uint64_t	 entry_padding;
 	int		 is_strtab;
 	int		 has_strtab;
+	char		 wrote_global_header;
 	char		*strtab;
 };
 
@@ -74,8 +75,8 @@
 			     struct archive_entry *);
 static ssize_t		 archive_write_ar_data(struct archive_write *,
 			     const void *buff, size_t s);
-static int		 archive_write_ar_destroy(struct archive_write *);
-static int		 archive_write_ar_finish(struct archive_write *);
+static int		 archive_write_ar_free(struct archive_write *);
+static int		 archive_write_ar_close(struct archive_write *);
 static int		 archive_write_ar_finish_entry(struct archive_write *);
 static const char	*ar_basename(const char *path);
 static int		 format_octal(int64_t v, char *p, int s);
@@ -85,7 +86,11 @@
 archive_write_set_format_ar_bsd(struct archive *_a)
 {
 	struct archive_write *a = (struct archive_write *)_a;
-	int r = archive_write_set_format_ar(a);
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_ar_bsd");
+	r = archive_write_set_format_ar(a);
 	if (r == ARCHIVE_OK) {
 		a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
 		a->archive.archive_format_name = "ar (BSD)";
@@ -97,7 +102,11 @@
 archive_write_set_format_ar_svr4(struct archive *_a)
 {
 	struct archive_write *a = (struct archive_write *)_a;
-	int r = archive_write_set_format_ar(a);
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_ar_svr4");
+	r = archive_write_set_format_ar(a);
 	if (r == ARCHIVE_OK) {
 		a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;
 		a->archive.archive_format_name = "ar (GNU/SVR4)";
@@ -114,8 +123,8 @@
 	struct ar_w *ar;
 
 	/* If someone else was already registered, unregister them. */
-	if (a->format_destroy != NULL)
-		(a->format_destroy)(a);
+	if (a->format_free != NULL)
+		(a->format_free)(a);
 
 	ar = (struct ar_w *)malloc(sizeof(*ar));
 	if (ar == NULL) {
@@ -128,8 +137,8 @@
 	a->format_name = "ar";
 	a->format_write_header = archive_write_ar_header;
 	a->format_write_data = archive_write_ar_data;
-	a->format_finish = archive_write_ar_finish;
-	a->format_destroy = archive_write_ar_destroy;
+	a->format_close = archive_write_ar_close;
+	a->format_free = archive_write_ar_free;
 	a->format_finish_entry = archive_write_ar_finish_entry;
 	return (ARCHIVE_OK);
 }
@@ -166,8 +175,10 @@
 	 * If we are now at the beginning of the archive,
 	 * we need first write the ar global header.
 	 */
-	if (a->archive.file_position == 0)
-		(a->compressor.write)(a, "!<arch>\n", 8);
+	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);
@@ -190,7 +201,7 @@
 		ar->is_strtab = 1;
 		buff[AR_name_offset] = buff[AR_name_offset + 1] = '/';
 		/*
-		 * For archive string table, only ar_size filed should
+		 * For archive string table, only ar_size field should
 		 * be set.
 		 */
 		goto size;
@@ -330,7 +341,7 @@
 		return (ARCHIVE_WARN);
 	}
 
-	ret = (a->compressor.write)(a, buff, 60);
+	ret = __archive_write_output(a, buff, 60);
 	if (ret != ARCHIVE_OK)
 		return (ret);
 
@@ -338,7 +349,7 @@
 	ar->entry_padding = ar->entry_bytes_remaining % 2;
 
 	if (append_fn > 0) {
-		ret = (a->compressor.write)(a, filename, strlen(filename));
+		ret = __archive_write_output(a, filename, strlen(filename));
 		if (ret != ARCHIVE_OK)
 			return (ret);
 		ar->entry_bytes_remaining -= strlen(filename);
@@ -374,7 +385,7 @@
 		ar->has_strtab = 1;
 	}
 
-	ret = (a->compressor.write)(a, buff, s);
+	ret = __archive_write_output(a, buff, s);
 	if (ret != ARCHIVE_OK)
 		return (ret);
 
@@ -383,7 +394,7 @@
 }
 
 static int
-archive_write_ar_destroy(struct archive_write *a)
+archive_write_ar_free(struct archive_write *a)
 {
 	struct ar_w *ar;
 
@@ -403,16 +414,19 @@
 }
 
 static int
-archive_write_ar_finish(struct archive_write *a)
+archive_write_ar_close(struct archive_write *a)
 {
+	struct ar_w *ar;
 	int ret;
 
 	/*
 	 * If we haven't written anything yet, we need to write
 	 * the ar global header now to make it a valid ar archive.
 	 */
-	if (a->archive.file_position == 0) {
-		ret = (a->compressor.write)(a, "!<arch>\n", 8);
+	ar = (struct ar_w *)a->format_data;
+	if (!ar->wrote_global_header) {
+		ar->wrote_global_header = 1;
+		ret = __archive_write_output(a, "!<arch>\n", 8);
 		return (ret);
 	}
 
@@ -439,12 +453,12 @@
 
 	if (ar->entry_padding != 1) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-		    "Padding wrong size: %d should be 1 or 0",
-		    (int)ar->entry_padding);
+		    "Padding wrong size: %ju should be 1 or 0",
+		    (uintmax_t)ar->entry_padding);
 		return (ARCHIVE_WARN);
 	}
 
-	ret = (a->compressor.write)(a, "\n", 1);
+	ret = __archive_write_output(a, "\n", 1);
 	return (ret);
 }
 
@@ -501,7 +515,7 @@
 	len = s;
 	h = p;
 
-	/* Negative values in ar header are meaningless , so use 0. */
+	/* Negative values in ar header are meaningless, so use 0. */
 	if (v < 0) {
 		while (len-- > 0)
 			*p++ = '0';
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write_set_format_by_name.c
--- a/head/contrib/libarchive/libarchive/archive_write_set_format_by_name.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write_set_format_by_name.c	Fri Mar 02 16:54:40 2012 +0200
@@ -24,7 +24,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format_by_name.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format_by_name.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -44,19 +44,28 @@
 static
 struct { const char *name; int (*setter)(struct archive *); } names[] =
 {
+	{ "7zip",	archive_write_set_format_7zip },
 	{ "ar",		archive_write_set_format_ar_bsd },
 	{ "arbsd",	archive_write_set_format_ar_bsd },
 	{ "argnu",	archive_write_set_format_ar_svr4 },
 	{ "arsvr4",	archive_write_set_format_ar_svr4 },
+	{ "bsdtar",	archive_write_set_format_pax_restricted },
+	{ "cd9660",	archive_write_set_format_iso9660 },
 	{ "cpio",	archive_write_set_format_cpio },
+	{ "gnutar",	archive_write_set_format_gnutar },
+	{ "iso",	archive_write_set_format_iso9660 },
+	{ "iso9660",	archive_write_set_format_iso9660 },
 	{ "mtree",	archive_write_set_format_mtree },
 	{ "newc",	archive_write_set_format_cpio_newc },
 	{ "odc",	archive_write_set_format_cpio },
 	{ "pax",	archive_write_set_format_pax },
+	{ "paxr",	archive_write_set_format_pax_restricted },
 	{ "posix",	archive_write_set_format_pax },
+	{ "rpax",	archive_write_set_format_pax_restricted },
 	{ "shar",	archive_write_set_format_shar },
 	{ "shardump",	archive_write_set_format_shar_dump },
 	{ "ustar",	archive_write_set_format_ustar },
+	{ "xar",	archive_write_set_format_xar },
 	{ "zip",	archive_write_set_format_zip },
 	{ NULL,		NULL }
 };
@@ -72,5 +81,6 @@
 	}
 
 	archive_set_error(a, EINVAL, "No such format '%s'", name);
+	a->state = ARCHIVE_STATE_FATAL;
 	return (ARCHIVE_FATAL);
 }
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write_set_format_cpio.c
--- a/head/contrib/libarchive/libarchive/archive_write_set_format_cpio.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write_set_format_cpio.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,7 +25,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format_cpio.c 228911 2011-12-27 10:36:56Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format_cpio.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -39,18 +40,22 @@
 
 #include "archive.h"
 #include "archive_entry.h"
+#include "archive_entry_locale.h"
 #include "archive_private.h"
 #include "archive_write_private.h"
 
 static ssize_t	archive_write_cpio_data(struct archive_write *,
 		    const void *buff, size_t s);
-static int	archive_write_cpio_finish(struct archive_write *);
-static int	archive_write_cpio_destroy(struct archive_write *);
+static int	archive_write_cpio_close(struct archive_write *);
+static int	archive_write_cpio_free(struct archive_write *);
 static int	archive_write_cpio_finish_entry(struct archive_write *);
 static int	archive_write_cpio_header(struct archive_write *,
 		    struct archive_entry *);
+static int	archive_write_cpio_options(struct archive_write *,
+		    const char *, const char *);
 static int	format_octal(int64_t, void *, int);
 static int64_t	format_octal_recursive(int64_t, char *, int);
+static int	write_header(struct archive_write *, struct archive_entry *);
 
 struct cpio {
 	uint64_t	  entry_bytes_remaining;
@@ -60,31 +65,34 @@
 	struct		 { int64_t old; int new;} *ino_list;
 	size_t		  ino_list_size;
 	size_t		  ino_list_next;
+
+	struct archive_string_conv *opt_sconv;
+	struct archive_string_conv *sconv_default;
+	int		  init_default_conversion;
 };
 
-#ifdef _MSC_VER
-#define __packed
-#pragma pack(push, 1)
-#endif
-
-struct cpio_header {
-	char	c_magic[6];
-	char	c_dev[6];
-	char	c_ino[6];
-	char	c_mode[6];
-	char	c_uid[6];
-	char	c_gid[6];
-	char	c_nlink[6];
-	char	c_rdev[6];
-	char	c_mtime[11];
-	char	c_namesize[6];
-	char	c_filesize[11];
-} __packed;
-
-#ifdef _MSC_VER
-#undef __packed
-#pragma pack(pop)
-#endif
+#define	c_magic_offset 0
+#define	c_magic_size 6
+#define	c_dev_offset 6
+#define	c_dev_size 6
+#define	c_ino_offset 12
+#define	c_ino_size 6
+#define	c_mode_offset 18
+#define	c_mode_size 6
+#define	c_uid_offset 24
+#define	c_uid_size 6
+#define	c_gid_offset 30
+#define	c_gid_size 6
+#define	c_nlink_offset 36
+#define	c_nlink_size 6
+#define	c_rdev_offset 42
+#define	c_rdev_size 6
+#define	c_mtime_offset 48
+#define	c_mtime_size 11
+#define	c_namesize_offset 59
+#define	c_namesize_size 6
+#define	c_filesize_offset 65
+#define	c_filesize_size 11
 
 /*
  * Set output format to 'cpio' format.
@@ -95,30 +103,60 @@
 	struct archive_write *a = (struct archive_write *)_a;
 	struct cpio *cpio;
 
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_cpio");
+
 	/* If someone else was already registered, unregister them. */
-	if (a->format_destroy != NULL)
-		(a->format_destroy)(a);
+	if (a->format_free != NULL)
+		(a->format_free)(a);
 
-	cpio = (struct cpio *)malloc(sizeof(*cpio));
+	cpio = (struct cpio *)calloc(1, sizeof(*cpio));
 	if (cpio == NULL) {
 		archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
 		return (ARCHIVE_FATAL);
 	}
-	memset(cpio, 0, sizeof(*cpio));
 	a->format_data = cpio;
-
-	a->pad_uncompressed = 1;
 	a->format_name = "cpio";
+	a->format_options = archive_write_cpio_options;
 	a->format_write_header = archive_write_cpio_header;
 	a->format_write_data = archive_write_cpio_data;
 	a->format_finish_entry = archive_write_cpio_finish_entry;
-	a->format_finish = archive_write_cpio_finish;
-	a->format_destroy = archive_write_cpio_destroy;
+	a->format_close = archive_write_cpio_close;
+	a->format_free = archive_write_cpio_free;
 	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
 	a->archive.archive_format_name = "POSIX cpio";
 	return (ARCHIVE_OK);
 }
 
+static int
+archive_write_cpio_options(struct archive_write *a, const char *key,
+    const char *val)
+{
+	struct cpio *cpio = (struct cpio *)a->format_data;
+	int ret = ARCHIVE_FAILED;
+
+	if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "%s: hdrcharset option needs a character-set name",
+			    a->format_name);
+		else {
+			cpio->opt_sconv = archive_string_conversion_to_charset(
+			    &a->archive, val, 0);
+			if (cpio->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
 /*
  * Ino values are as long as 64 bits on some systems; cpio format
  * only allows 18 bits and relies on the ino values to identify hardlinked
@@ -184,78 +222,195 @@
 	return (ino_new);
 }
 
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a)
+{
+	struct cpio *cpio;
+	struct archive_string_conv *sconv;
+
+	cpio = (struct cpio *)a->format_data;
+	sconv = cpio->opt_sconv;
+	if (sconv == NULL) {
+		if (!cpio->init_default_conversion) {
+			cpio->sconv_default =
+			    archive_string_default_conversion_for_write(
+			      &(a->archive));
+			cpio->init_default_conversion = 1;
+		}
+		sconv = cpio->sconv_default;
+	}
+	return (sconv);
+}
+
 static int
 archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry)
 {
+	const char *path;
+	size_t len;
+
+	if (archive_entry_filetype(entry) == 0) {
+		archive_set_error(&a->archive, -1, "Filetype required");
+		return (ARCHIVE_FAILED);
+	}
+
+	if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
+	    && errno == ENOMEM) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for Pathname");
+		return (ARCHIVE_FATAL);
+	}
+	if (len == 0 || path == NULL || path[0] == '\0') {
+		archive_set_error(&a->archive, -1, "Pathname required");
+		return (ARCHIVE_FAILED);
+	}
+
+	if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0) {
+		archive_set_error(&a->archive, -1, "Size required");
+		return (ARCHIVE_FAILED);
+	}
+	return write_header(a, entry);
+}
+
+static int
+write_header(struct archive_write *a, struct archive_entry *entry)
+{
 	struct cpio *cpio;
 	const char *p, *path;
-	int pathlength, ret, ret2;
+	int pathlength, ret, ret_final;
 	int64_t	ino;
-	struct cpio_header	 h;
+	char h[76];
+	struct archive_string_conv *sconv;
+	struct archive_entry *entry_main;
+	size_t len;
 
 	cpio = (struct cpio *)a->format_data;
-	ret2 = ARCHIVE_OK;
+	ret_final = ARCHIVE_OK;
+	sconv = get_sconv(a);
 
-	path = archive_entry_pathname(entry);
-	pathlength = (int)strlen(path) + 1; /* Include trailing null. */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* Make sure the path separators in pahtname, 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
 
-	memset(&h, 0, sizeof(h));
-	format_octal(070707, &h.c_magic, sizeof(h.c_magic));
-	format_octal(archive_entry_dev(entry), &h.c_dev, sizeof(h.c_dev));
+	ret = archive_entry_pathname_l(entry, &path, &len, sconv);
+	if (ret != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			ret_final = 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));
+		ret_final = ARCHIVE_WARN;
+	}
+	/* Include trailing null. */
+	pathlength = (int)len + 1;
+
+	memset(h, 0, sizeof(h));
+	format_octal(070707, h + c_magic_offset, c_magic_size);
+	format_octal(archive_entry_dev(entry), h + c_dev_offset, c_dev_size);
 
 	ino = synthesize_ino_value(cpio, entry);
 	if (ino < 0) {
 		archive_set_error(&a->archive, ENOMEM,
 		    "No memory for ino translation table");
-		return (ARCHIVE_FATAL);
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
 	} else if (ino > 0777777) {
 		archive_set_error(&a->archive, ERANGE,
 		    "Too many files for this cpio format");
-		return (ARCHIVE_FATAL);
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
 	}
-	format_octal(ino & 0777777, &h.c_ino, sizeof(h.c_ino));
+	format_octal(ino & 0777777, h + c_ino_offset, c_ino_size);
 
-	format_octal(archive_entry_mode(entry), &h.c_mode, sizeof(h.c_mode));
-	format_octal(archive_entry_uid(entry), &h.c_uid, sizeof(h.c_uid));
-	format_octal(archive_entry_gid(entry), &h.c_gid, sizeof(h.c_gid));
-	format_octal(archive_entry_nlink(entry), &h.c_nlink, sizeof(h.c_nlink));
+	/* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */
+	format_octal(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);
+	format_octal(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);
+	format_octal(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);
+	format_octal(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);
 	if (archive_entry_filetype(entry) == AE_IFBLK
 	    || archive_entry_filetype(entry) == AE_IFCHR)
-	    format_octal(archive_entry_dev(entry), &h.c_rdev, sizeof(h.c_rdev));
+	    format_octal(archive_entry_dev(entry), h + c_rdev_offset, c_rdev_size);
 	else
-	    format_octal(0, &h.c_rdev, sizeof(h.c_rdev));
-	format_octal(archive_entry_mtime(entry), &h.c_mtime, sizeof(h.c_mtime));
-	format_octal(pathlength, &h.c_namesize, sizeof(h.c_namesize));
+	    format_octal(0, h + c_rdev_offset, c_rdev_size);
+	format_octal(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);
+	format_octal(pathlength, h + c_namesize_offset, c_namesize_size);
 
 	/* Non-regular files don't store bodies. */
 	if (archive_entry_filetype(entry) != AE_IFREG)
 		archive_entry_set_size(entry, 0);
 
 	/* Symlinks get the link written as the body of the entry. */
-	p = archive_entry_symlink(entry);
-	if (p != NULL  &&  *p != '\0')
-		format_octal(strlen(p), &h.c_filesize, sizeof(h.c_filesize));
+	ret = archive_entry_symlink_l(entry, &p, &len, sconv);
+	if (ret != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate linkname '%s' to %s",
+		    archive_entry_symlink(entry),
+		    archive_string_conversion_charset_name(sconv));
+		ret_final = ARCHIVE_WARN;
+	}
+	if (len > 0 && p != NULL  &&  *p != '\0')
+		ret = format_octal(strlen(p), h + c_filesize_offset,
+		    c_filesize_size);
 	else
-		format_octal(archive_entry_size(entry),
-		    &h.c_filesize, sizeof(h.c_filesize));
+		ret = format_octal(archive_entry_size(entry),
+		    h + c_filesize_offset, c_filesize_size);
+	if (ret) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File is too large for cpio format.");
+		ret_final = ARCHIVE_FAILED;
+		goto exit_write_header;
+	}
 
-	ret = (a->compressor.write)(a, &h, sizeof(h));
-	if (ret != ARCHIVE_OK)
-		return (ARCHIVE_FATAL);
+	ret = __archive_write_output(a, h, sizeof(h));
+	if (ret != ARCHIVE_OK) {
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
 
-	ret = (a->compressor.write)(a, path, pathlength);
-	if (ret != ARCHIVE_OK)
-		return (ARCHIVE_FATAL);
+	ret = __archive_write_output(a, path, pathlength);
+	if (ret != ARCHIVE_OK) {
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
 
 	cpio->entry_bytes_remaining = archive_entry_size(entry);
 
 	/* Write the symlink now. */
-	if (p != NULL  &&  *p != '\0')
-		ret = (a->compressor.write)(a, p, strlen(p));
-
-	if (ret == ARCHIVE_OK)
-		ret = ret2;
-	return (ret);
+	if (p != NULL  &&  *p != '\0') {
+		ret = __archive_write_output(a, p, strlen(p));
+		if (ret != ARCHIVE_OK) {
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+	}
+exit_write_header:
+	if (entry_main)
+		archive_entry_free(entry_main);
+	return (ret_final);
 }
 
 static ssize_t
@@ -268,7 +423,7 @@
 	if (s > cpio->entry_bytes_remaining)
 		s = cpio->entry_bytes_remaining;
 
-	ret = (a->compressor.write)(a, buff, s);
+	ret = __archive_write_output(a, buff, s);
 	cpio->entry_bytes_remaining -= s;
 	if (ret >= 0)
 		return (s);
@@ -307,22 +462,23 @@
 }
 
 static int
-archive_write_cpio_finish(struct archive_write *a)
+archive_write_cpio_close(struct archive_write *a)
 {
 	int er;
 	struct archive_entry *trailer;
 
-	trailer = archive_entry_new();
+	trailer = archive_entry_new2(NULL);
 	/* nlink = 1 here for GNU cpio compat. */
 	archive_entry_set_nlink(trailer, 1);
+	archive_entry_set_size(trailer, 0);
 	archive_entry_set_pathname(trailer, "TRAILER!!!");
-	er = archive_write_cpio_header(a, trailer);
+	er = write_header(a, trailer);
 	archive_entry_free(trailer);
 	return (er);
 }
 
 static int
-archive_write_cpio_destroy(struct archive_write *a)
+archive_write_cpio_free(struct archive_write *a)
 {
 	struct cpio *cpio;
 
@@ -337,18 +493,7 @@
 archive_write_cpio_finish_entry(struct archive_write *a)
 {
 	struct cpio *cpio;
-	size_t to_write;
-	int ret;
 
 	cpio = (struct cpio *)a->format_data;
-	ret = ARCHIVE_OK;
-	while (cpio->entry_bytes_remaining > 0) {
-		to_write = cpio->entry_bytes_remaining < a->null_length ?
-		    cpio->entry_bytes_remaining : a->null_length;
-		ret = (a->compressor.write)(a, a->nulls, to_write);
-		if (ret != ARCHIVE_OK)
-			return (ret);
-		cpio->entry_bytes_remaining -= to_write;
-	}
-	return (ret);
+	return (__archive_write_nulls(a, cpio->entry_bytes_remaining));
 }
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c
--- a/head/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
  * Copyright (c) 2006 Rudolf Marek SYSGO s.r.o.
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,7 +26,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -40,40 +41,61 @@
 
 #include "archive.h"
 #include "archive_entry.h"
+#include "archive_entry_locale.h"
 #include "archive_private.h"
 #include "archive_write_private.h"
 
 static ssize_t	archive_write_newc_data(struct archive_write *,
 		    const void *buff, size_t s);
-static int	archive_write_newc_finish(struct archive_write *);
-static int	archive_write_newc_destroy(struct archive_write *);
+static int	archive_write_newc_close(struct archive_write *);
+static int	archive_write_newc_free(struct archive_write *);
 static int	archive_write_newc_finish_entry(struct archive_write *);
 static int	archive_write_newc_header(struct archive_write *,
 		    struct archive_entry *);
+static int      archive_write_newc_options(struct archive_write *,
+		    const char *, const char *);
 static int	format_hex(int64_t, void *, int);
 static int64_t	format_hex_recursive(int64_t, char *, int);
+static int	write_header(struct archive_write *, struct archive_entry *);
 
 struct cpio {
 	uint64_t	  entry_bytes_remaining;
 	int		  padding;
+
+	struct archive_string_conv *opt_sconv;
+	struct archive_string_conv *sconv_default;
+	int		  init_default_conversion;
 };
 
-struct cpio_header_newc {
-	char	c_magic[6];
-	char	c_ino[8];
-	char	c_mode[8];
-	char	c_uid[8];
-	char	c_gid[8];
-	char	c_nlink[8];
-	char	c_mtime[8];
-	char	c_filesize[8];
-	char	c_devmajor[8];
-	char	c_devminor[8];
-	char	c_rdevmajor[8];
-	char	c_rdevminor[8];
-	char	c_namesize[8];
-	char	c_checksum[8];
-};
+#define	c_magic_offset 0
+#define	c_magic_size 6
+#define	c_ino_offset 6
+#define	c_ino_size 8
+#define	c_mode_offset 14
+#define	c_mode_size 8
+#define	c_uid_offset 22
+#define	c_uid_size 8
+#define	c_gid_offset 30
+#define	c_gid_size 8
+#define	c_nlink_offset 38
+#define	c_nlink_size 8
+#define	c_mtime_offset 46
+#define	c_mtime_size 8
+#define	c_filesize_offset 54
+#define	c_filesize_size 8
+#define	c_devmajor_offset 62
+#define	c_devmajor_size 8
+#define	c_devminor_offset 70
+#define	c_devminor_size 8
+#define	c_rdevmajor_offset 78
+#define	c_rdevmajor_size 8
+#define	c_rdevminor_offset 86
+#define	c_rdevminor_size 8
+#define	c_namesize_offset 94
+#define	c_namesize_size 8
+#define	c_checksum_offset 102
+#define	c_checksum_size 8
+#define	c_header_size 110
 
 /* Logic trick: difference between 'n' and next multiple of 4 */
 #define PAD4(n)	(3 & (1 + ~(n)))
@@ -87,9 +109,12 @@
 	struct archive_write *a = (struct archive_write *)_a;
 	struct cpio *cpio;
 
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_newc");
+
 	/* If someone else was already registered, unregister them. */
-	if (a->format_destroy != NULL)
-		(a->format_destroy)(a);
+	if (a->format_free != NULL)
+		(a->format_free)(a);
 
 	cpio = (struct cpio *)malloc(sizeof(*cpio));
 	if (cpio == NULL) {
@@ -98,107 +123,253 @@
 	}
 	memset(cpio, 0, sizeof(*cpio));
 	a->format_data = cpio;
-
-	a->pad_uncompressed = 1;
 	a->format_name = "cpio";
+	a->format_options = archive_write_newc_options;
 	a->format_write_header = archive_write_newc_header;
 	a->format_write_data = archive_write_newc_data;
 	a->format_finish_entry = archive_write_newc_finish_entry;
-	a->format_finish = archive_write_newc_finish;
-	a->format_destroy = archive_write_newc_destroy;
+	a->format_close = archive_write_newc_close;
+	a->format_free = archive_write_newc_free;
 	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
 	a->archive.archive_format_name = "SVR4 cpio nocrc";
 	return (ARCHIVE_OK);
 }
 
 static int
+archive_write_newc_options(struct archive_write *a, const char *key,
+    const char *val)
+{
+	struct cpio *cpio = (struct cpio *)a->format_data;
+	int ret = ARCHIVE_FAILED;
+
+	if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "%s: hdrcharset option needs a character-set name",
+			    a->format_name);
+		else {
+			cpio->opt_sconv = archive_string_conversion_to_charset(
+			    &a->archive, val, 0);
+			if (cpio->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a)
+{
+	struct cpio *cpio;
+	struct archive_string_conv *sconv;
+
+	cpio = (struct cpio *)a->format_data;
+	sconv = cpio->opt_sconv;
+	if (sconv == NULL) {
+		if (!cpio->init_default_conversion) {
+			cpio->sconv_default =
+			    archive_string_default_conversion_for_write(
+			      &(a->archive));
+			cpio->init_default_conversion = 1;
+		}
+		sconv = cpio->sconv_default;
+	}
+	return (sconv);
+}
+
+static int
 archive_write_newc_header(struct archive_write *a, struct archive_entry *entry)
 {
+	const char *path;
+	size_t len;
+
+	if (archive_entry_filetype(entry) == 0) {
+		archive_set_error(&a->archive, -1, "Filetype required");
+		return (ARCHIVE_FAILED);
+	}
+
+	if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
+	    && errno == ENOMEM) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for Pathname");
+		return (ARCHIVE_FATAL);
+	}
+	if (len == 0 || path == NULL || path[0] == '\0') {
+		archive_set_error(&a->archive, -1, "Pathname required");
+		return (ARCHIVE_FAILED);
+	}
+
+	if (archive_entry_hardlink(entry) == NULL
+	    && (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0)) {
+		archive_set_error(&a->archive, -1, "Size required");
+		return (ARCHIVE_FAILED);
+	}
+	return write_header(a, entry);
+}
+
+static int
+write_header(struct archive_write *a, struct archive_entry *entry)
+{
 	int64_t ino;
 	struct cpio *cpio;
 	const char *p, *path;
-	int pathlength, ret, ret2;
-	struct cpio_header_newc	 h;
+	int pathlength, ret, ret_final;
+	char h[c_header_size];
+	struct archive_string_conv *sconv;
+	struct archive_entry *entry_main;
+	size_t len;
 	int pad;
 
 	cpio = (struct cpio *)a->format_data;
-	ret2 = ARCHIVE_OK;
+	ret_final = ARCHIVE_OK;
+	sconv = get_sconv(a);
 
-	path = archive_entry_pathname(entry);
-	pathlength = (int)strlen(path) + 1; /* Include trailing null. */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* Make sure the path separators in pahtname, 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
 
-	memset(&h, 0, sizeof(h));
-	format_hex(0x070701, &h.c_magic, sizeof(h.c_magic));
-	format_hex(archive_entry_devmajor(entry), &h.c_devmajor,
-	    sizeof(h.c_devmajor));
-	format_hex(archive_entry_devminor(entry), &h.c_devminor,
-	    sizeof(h.c_devminor));
+	ret = archive_entry_pathname_l(entry, &path, &len, sconv);
+	if (ret != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			ret_final = 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));
+		ret_final = ARCHIVE_WARN;
+	}
+	pathlength = (int)len + 1; /* Include trailing null. */
+
+	memset(h, 0, c_header_size);
+	format_hex(0x070701, h + c_magic_offset, c_magic_size);
+	format_hex(archive_entry_devmajor(entry), h + c_devmajor_offset,
+	    c_devmajor_size);
+	format_hex(archive_entry_devminor(entry), h + c_devminor_offset,
+	    c_devminor_size);
 
 	ino = archive_entry_ino64(entry);
 	if (ino > 0xffffffff) {
 		archive_set_error(&a->archive, ERANGE,
 		    "large inode number truncated");
-		ret2 = ARCHIVE_WARN;
+		ret_final = ARCHIVE_WARN;
 	}
 
-	format_hex(ino & 0xffffffff, &h.c_ino, sizeof(h.c_ino));
-	format_hex(archive_entry_mode(entry), &h.c_mode, sizeof(h.c_mode));
-	format_hex(archive_entry_uid(entry), &h.c_uid, sizeof(h.c_uid));
-	format_hex(archive_entry_gid(entry), &h.c_gid, sizeof(h.c_gid));
-	format_hex(archive_entry_nlink(entry), &h.c_nlink, sizeof(h.c_nlink));
+	/* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */
+	format_hex(ino & 0xffffffff, h + c_ino_offset, c_ino_size);
+	format_hex(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);
+	format_hex(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);
+	format_hex(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);
+	format_hex(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);
 	if (archive_entry_filetype(entry) == AE_IFBLK
 	    || archive_entry_filetype(entry) == AE_IFCHR) {
-	    format_hex(archive_entry_rdevmajor(entry), &h.c_rdevmajor, sizeof(h.c_rdevmajor));
-	    format_hex(archive_entry_rdevminor(entry), &h.c_rdevminor, sizeof(h.c_rdevminor));
+	    format_hex(archive_entry_rdevmajor(entry), h + c_rdevmajor_offset, c_rdevmajor_size);
+	    format_hex(archive_entry_rdevminor(entry), h + c_rdevminor_offset, c_rdevminor_size);
 	} else {
-	    format_hex(0, &h.c_rdevmajor, sizeof(h.c_rdevmajor));
-	    format_hex(0, &h.c_rdevminor, sizeof(h.c_rdevminor));
+	    format_hex(0, h + c_rdevmajor_offset, c_rdevmajor_size);
+	    format_hex(0, h + c_rdevminor_offset, c_rdevminor_size);
 	}
-	format_hex(archive_entry_mtime(entry), &h.c_mtime, sizeof(h.c_mtime));
-	format_hex(pathlength, &h.c_namesize, sizeof(h.c_namesize));
-	format_hex(0, &h.c_checksum, sizeof(h.c_checksum));
+	format_hex(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);
+	format_hex(pathlength, h + c_namesize_offset, c_namesize_size);
+	format_hex(0, h + c_checksum_offset, c_checksum_size);
 
 	/* Non-regular files don't store bodies. */
 	if (archive_entry_filetype(entry) != AE_IFREG)
 		archive_entry_set_size(entry, 0);
 
 	/* Symlinks get the link written as the body of the entry. */
-	p = archive_entry_symlink(entry);
-	if (p != NULL  &&  *p != '\0')
-		format_hex(strlen(p), &h.c_filesize, sizeof(h.c_filesize));
+	ret = archive_entry_symlink_l(entry, &p, &len, sconv);
+	if (ret != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Likname");
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate linkname '%s' to %s",
+		    archive_entry_symlink(entry),
+		    archive_string_conversion_charset_name(sconv));
+		ret_final = ARCHIVE_WARN;
+	}
+	if (len > 0 && p != NULL  &&  *p != '\0')
+		ret = format_hex(strlen(p), h + c_filesize_offset,
+		    c_filesize_size);
 	else
-		format_hex(archive_entry_size(entry),
-		    &h.c_filesize, sizeof(h.c_filesize));
+		ret = format_hex(archive_entry_size(entry),
+		    h + c_filesize_offset, c_filesize_size);
+	if (ret) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File is too large for this format.");
+		ret_final = ARCHIVE_FAILED;
+		goto exit_write_header;
+	}
 
-	ret = (a->compressor.write)(a, &h, sizeof(h));
-	if (ret != ARCHIVE_OK)
-		return (ARCHIVE_FATAL);
+	ret = __archive_write_output(a, h, c_header_size);
+	if (ret != ARCHIVE_OK) {
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
 
 	/* Pad pathname to even length. */
-	ret = (a->compressor.write)(a, path, pathlength);
-	if (ret != ARCHIVE_OK)
-		return (ARCHIVE_FATAL);
-	pad = PAD4(pathlength + sizeof(struct cpio_header_newc));
-	if (pad)
-		ret = (a->compressor.write)(a, "\0\0\0", pad);
-	if (ret != ARCHIVE_OK)
-		return (ARCHIVE_FATAL);
+	ret = __archive_write_output(a, path, pathlength);
+	if (ret != ARCHIVE_OK) {
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
+	pad = PAD4(pathlength + c_header_size);
+	if (pad) {
+		ret = __archive_write_output(a, "\0\0\0", pad);
+		if (ret != ARCHIVE_OK) {
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+	}
 
 	cpio->entry_bytes_remaining = archive_entry_size(entry);
 	cpio->padding = PAD4(cpio->entry_bytes_remaining);
 
 	/* Write the symlink now. */
 	if (p != NULL  &&  *p != '\0') {
-		ret = (a->compressor.write)(a, p, strlen(p));
-		if (ret != ARCHIVE_OK)
-			return (ARCHIVE_FATAL);
+		ret = __archive_write_output(a, p, strlen(p));
+		if (ret != ARCHIVE_OK) {
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
 		pad = PAD4(strlen(p));
-		ret = (a->compressor.write)(a, "\0\0\0", pad);
+		ret = __archive_write_output(a, "\0\0\0", pad);
+		if (ret != ARCHIVE_OK) {
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
 	}
-
-	if (ret == ARCHIVE_OK)
-		ret = ret2;
-	return (ret);
+exit_write_header:
+	if (entry_main)
+		archive_entry_free(entry_main);
+	return (ret_final);
 }
 
 static ssize_t
@@ -211,7 +382,7 @@
 	if (s > cpio->entry_bytes_remaining)
 		s = cpio->entry_bytes_remaining;
 
-	ret = (a->compressor.write)(a, buff, s);
+	ret = __archive_write_output(a, buff, s);
 	cpio->entry_bytes_remaining -= s;
 	if (ret >= 0)
 		return (s);
@@ -250,21 +421,23 @@
 }
 
 static int
-archive_write_newc_finish(struct archive_write *a)
+archive_write_newc_close(struct archive_write *a)
 {
 	int er;
 	struct archive_entry *trailer;
 
 	trailer = archive_entry_new();
 	archive_entry_set_nlink(trailer, 1);
+	archive_entry_set_size(trailer, 0);
 	archive_entry_set_pathname(trailer, "TRAILER!!!");
-	er = archive_write_newc_header(a, trailer);
+	/* Bypass the required data checks. */
+	er = write_header(a, trailer);
 	archive_entry_free(trailer);
 	return (er);
 }
 
 static int
-archive_write_newc_destroy(struct archive_write *a)
+archive_write_newc_free(struct archive_write *a)
 {
 	struct cpio *cpio;
 
@@ -278,18 +451,7 @@
 archive_write_newc_finish_entry(struct archive_write *a)
 {
 	struct cpio *cpio;
-	size_t to_write;
-	int ret;
 
 	cpio = (struct cpio *)a->format_data;
-	while (cpio->entry_bytes_remaining > 0) {
-		to_write = cpio->entry_bytes_remaining < a->null_length ?
-		    cpio->entry_bytes_remaining : a->null_length;
-		ret = (a->compressor.write)(a, a->nulls, to_write);
-		if (ret != ARCHIVE_OK)
-			return (ret);
-		cpio->entry_bytes_remaining -= to_write;
-	}
-	ret = (a->compressor.write)(a, a->nulls, cpio->padding);
-	return (ret);
+	return (__archive_write_nulls(a, cpio->entry_bytes_remaining + cpio->padding));
 }
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write_set_format_mtree.c
--- a/head/contrib/libarchive/libarchive/archive_write_set_format_mtree.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write_set_format_mtree.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,6 +1,6 @@
 /*-
- * Copyright (c) 2009 Michihiro NAKAJIMA
  * Copyright (c) 2008 Joerg Sonnenberger
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,7 +25,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format_mtree.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format_mtree.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -35,17 +35,68 @@
 #include <string.h>
 
 #include "archive.h"
+#include "archive_crypto_private.h"
 #include "archive_entry.h"
 #include "archive_private.h"
 #include "archive_write_private.h"
 
-#include "archive_hash.h"
-
 #define INDENTNAMELEN	15
 #define MAXLINELEN	80
+#define SET_KEYS	\
+	(F_FLAGS | F_GID | F_GNAME | F_MODE | F_TYPE | F_UID | F_UNAME)
+
+struct mtree_entry {
+	struct mtree_entry *next;
+
+	char *pathname;
+	char *symlink;
+	unsigned int nlink;
+	mode_t filetype;
+	mode_t mode;
+	int64_t uid;
+	int64_t gid;
+	char *uname;
+	char *gname;
+	char *fflags_text;
+	unsigned long fflags_set;
+	unsigned long fflags_clear;
+	time_t mtime;
+	long mtime_nsec;
+	dev_t rdevmajor;
+	dev_t rdevminor;
+	int64_t size;
+
+	int compute_sum;
+	uint32_t crc;
+#ifdef ARCHIVE_HAS_MD5
+	unsigned char buf_md5[16];
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	unsigned char buf_rmd160[20];
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	unsigned char buf_sha1[20];
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	unsigned char buf_sha256[32];
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	unsigned char buf_sha384[48];
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	unsigned char buf_sha512[64];
+#endif
+};
+
+struct attr_counter {
+	struct attr_counter *prev;
+	struct attr_counter *next;
+	int count;
+	struct mtree_entry *m_entry;
+};
 
 struct mtree_writer {
-	struct archive_entry *entry;
+	struct mtree_entry *mtree_entry;
 	struct archive_string ebuf;
 	struct archive_string buf;
 	int first;
@@ -56,13 +107,20 @@
 		struct archive_string parent;
 		mode_t		type;
 		int		keys;
-		uid_t		uid;
-		gid_t		gid;
+		int64_t		uid;
+		int64_t		gid;
 		mode_t		mode;
 		unsigned long	fflags_set;
 		unsigned long	fflags_clear;
+
+		struct attr_counter *uid_list;
+		struct attr_counter *gid_list;
+		struct attr_counter *mode_list;
+		struct attr_counter *flags_list;
+		struct mtree_entry *me_first;
+		struct mtree_entry **me_last;
 	} set;
-	/* chekc sum */
+	/* check sum */
 	int compute_sum;
 	uint32_t crc;
 	uint64_t crc_len;
@@ -125,6 +183,18 @@
 			 | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\
 			 | F_UNAME)
 
+static struct attr_counter * new_attr_count(struct mtree_entry *,
+	struct attr_counter *);
+static void free_attr_count(struct attr_counter **);
+static int inc_attr_count(struct attr_counter **, struct attr_counter *,
+	struct attr_counter *, struct mtree_entry *);
+static int collect_set_values(struct mtree_writer *, struct mtree_entry *);
+static int get_keys(struct mtree_writer *, struct mtree_entry *);
+static void sum_init(struct mtree_writer *);
+static void sum_update(struct mtree_writer *, const void *, size_t);
+static void sum_final(struct mtree_writer *, struct mtree_entry *);
+static void sum_write(struct archive_string *, struct mtree_entry *);
+
 #define	COMPUTE_CRC(var, ch)	(var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
 static const uint32_t crctab[] = {
 	0x0,
@@ -227,6 +297,9 @@
 		archive_strncat(s, start, str - start);
 }
 
+/*
+ * Indent a line as mtree utility to be readable for people.
+ */
 static void
 mtree_indent(struct mtree_writer *mtree)
 {
@@ -281,11 +354,11 @@
 
 #if !defined(_WIN32) || defined(__CYGWIN__)
 static size_t
-dir_len(struct archive_entry *entry)
+dir_len(struct mtree_entry *me)
 {
 	const char *path, *r;
 
-	path = archive_entry_pathname(entry);
+	path = me->pathname;
 	r = strrchr(path, '/');
 	if (r == NULL)
 		return (0);
@@ -301,14 +374,14 @@
  * code.
  */
 static size_t
-dir_len(struct archive_entry *entry)
+dir_len(struct mtree_entry *me)
 {
 	wchar_t wc;
 	const char *path;
 	const char *p, *rp;
 	size_t al, l, size;
 
-	path = archive_entry_pathname(entry);
+	path = me->pathname;
 	al = l = -1;
 	for (p = path; *p != '\0'; ++p) {
 		if (*p == '\\')
@@ -337,14 +410,17 @@
 }
 #endif /* _WIN32 && !__CYGWIN__ */
 
+/*
+ * Test if a parent directory of the current entry is changed.
+ */
 static int
-parent_dir_changed(struct archive_string *dir, struct archive_entry *entry)
+parent_dir_changed(struct archive_string *dir, struct mtree_entry *me)
 {
 	const char *path;
 	size_t l;
 
-	l = dir_len(entry);
-	path = archive_entry_pathname(entry);
+	l = dir_len(me);
+	path = me->pathname;
 	if (archive_strlen(dir) > 0) {
 		if (l == 0) {
 			archive_string_empty(dir);
@@ -359,129 +435,141 @@
 }
 
 /*
- * Write /set keyword. It means set global datas.
- * [directory-only mode]
- *   - It is only once to write /set keyword. It is using values of the
- *     first entry.
- * [normal mode]
- *   - Write /set keyword. It is using values of the first entry whose
- *     filetype is a regular file.
- *   - When a parent directory of the entry whose filetype is the regular
- *     file is changed, check the global datas and write it again if its
- *     values are different from the entry's.
+ * Write /set keyword.
+ * Set most used value of uid,gid,mode and fflags, which are
+ * collected by collect_set_values() function.
  */
 static void
-set_global(struct mtree_writer *mtree, struct archive_entry *entry)
+write_global(struct mtree_writer *mtree)
 {
 	struct archive_string setstr;
 	struct archive_string unsetstr;
 	const char *name;
 	int keys, oldkeys, effkeys;
-	mode_t set_type = 0;
-
-	switch (archive_entry_filetype(entry)) {
-	case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR:
-	case AE_IFBLK: case AE_IFIFO:
-		break;
-	case AE_IFDIR:
-		if (mtree->dironly)
-			set_type = AE_IFDIR;
-		break;
-	case AE_IFREG:
-	default:	/* Handle unknown file types as regular files. */
-		if (!mtree->dironly)
-			set_type = AE_IFREG;
-		break;
-	}
-	if (set_type == 0)
-		return;
-	if (mtree->set.processed &&
-	    !parent_dir_changed(&mtree->set.parent, entry))
-		return;
-	/* At first, save a parent directory of the entry for following
-	 * entries. */
-	if (!mtree->set.processed && set_type == AE_IFREG)
-		parent_dir_changed(&mtree->set.parent, entry);
+	struct attr_counter *ac;
 
 	archive_string_init(&setstr);
 	archive_string_init(&unsetstr);
-	keys = mtree->keys & (F_FLAGS | F_GID | F_GNAME | F_NLINK | F_MODE
-	    | F_TYPE | F_UID | F_UNAME);
+	keys = mtree->keys & SET_KEYS;
 	oldkeys = mtree->set.keys;
 	effkeys = keys;
 	if (mtree->set.processed) {
 		/*
-		 * Check the global datas for whether it needs updating.
+		 * Check if the global data needs updating.
 		 */
 		effkeys &= ~F_TYPE;
-		if ((oldkeys & (F_UNAME | F_UID)) != 0 &&
-		    mtree->set.uid == archive_entry_uid(entry))
-			effkeys &= ~(F_UNAME | F_UID);
-		if ((oldkeys & (F_GNAME | F_GID)) != 0 &&
-		    mtree->set.gid == archive_entry_gid(entry))
-			effkeys &= ~(F_GNAME | F_GID);
-		if ((oldkeys & F_MODE) != 0 &&
-		    mtree->set.mode == (archive_entry_mode(entry) & 07777))
-			effkeys &= ~F_MODE;
+		if (oldkeys & (F_UNAME | F_UID)) {
+			ac = mtree->set.uid_list;
+			do {
+				if (mtree->set.uid == ac->m_entry->uid) {
+					effkeys &= ~(F_UNAME | F_UID);
+					break;
+				}
+				if (ac->next != NULL &&
+				    ac->next->count == ac->count)
+					continue;
+			} while (0);
+		}
+		if (oldkeys & (F_GNAME | F_GID)) {
+			ac = mtree->set.gid_list;
+			do {
+				if (mtree->set.gid == ac->m_entry->gid) {
+					effkeys &= ~(F_GNAME | F_GID);
+					break;
+				}
+				if (ac->next != NULL &&
+				    ac->next->count == ac->count)
+					continue;
+			} while (0);
+		}
+		if (oldkeys & F_MODE) {
+			ac = mtree->set.mode_list;
+			do {
+				if (mtree->set.mode == ac->m_entry->mode) {
+					effkeys &= ~F_MODE;
+					break;
+				}
+				if (ac->next != NULL &&
+				    ac->next->count == ac->count)
+					continue;
+			} while (0);
+		}
 		if ((oldkeys & F_FLAGS) != 0) {
-			unsigned long	fflags_set;
-			unsigned long	fflags_clear;
-
-			archive_entry_fflags(entry, &fflags_set, &fflags_clear);
-			if (fflags_set == mtree->set.fflags_set &&
-			    fflags_clear == mtree->set.fflags_clear)
-				effkeys &= ~F_FLAGS;
+			ac = mtree->set.flags_list;
+			do {
+				if (ac->m_entry->fflags_set ==
+					mtree->set.fflags_set &&
+				    ac->m_entry->fflags_clear ==
+					mtree->set.fflags_clear) {
+					effkeys &= ~F_FLAGS;
+					break;
+				}
+				if (ac->next != NULL &&
+				    ac->next->count == ac->count)
+					continue;
+			} while (0);
 		}
 	}
 	if ((keys & effkeys & F_TYPE) != 0) {
-		mtree->set.type = set_type;
-		if (set_type == AE_IFDIR)
+		if (mtree->dironly) {
 			archive_strcat(&setstr, " type=dir");
-		else
+			mtree->set.type = AE_IFDIR;
+		} else {
 			archive_strcat(&setstr, " type=file");
+			mtree->set.type = AE_IFREG;
+		}
 	}
 	if ((keys & effkeys & F_UNAME) != 0) {
-		if ((name = archive_entry_uname(entry)) != NULL) {
+		name = mtree->set.uid_list->m_entry->uname;
+		if (name != NULL) {
 			archive_strcat(&setstr, " uname=");
 			mtree_quote(&setstr, name);
-		} else if ((oldkeys & F_UNAME) != 0)
-			archive_strcat(&unsetstr, " uname");
-		else
+		} else {
 			keys &= ~F_UNAME;
+			if ((oldkeys & F_UNAME) != 0)
+				archive_strcat(&unsetstr, " uname");
+		}
 	}
 	if ((keys & effkeys & F_UID) != 0) {
-		mtree->set.uid = archive_entry_uid(entry);
+		mtree->set.uid = mtree->set.uid_list->m_entry->uid;
 		archive_string_sprintf(&setstr, " uid=%jd",
 		    (intmax_t)mtree->set.uid);
 	}
 	if ((keys & effkeys & F_GNAME) != 0) {
-		if ((name = archive_entry_gname(entry)) != NULL) {
+		name = mtree->set.gid_list->m_entry->gname;
+		if (name != NULL) {
 			archive_strcat(&setstr, " gname=");
 			mtree_quote(&setstr, name);
-		} else if ((oldkeys & F_GNAME) != 0)
-			archive_strcat(&unsetstr, " gname");
-		else
+		} else {
 			keys &= ~F_GNAME;
+			if ((oldkeys & F_GNAME) != 0)
+				archive_strcat(&unsetstr, " gname");
+		}
 	}
 	if ((keys & effkeys & F_GID) != 0) {
-		mtree->set.gid = archive_entry_gid(entry);
+		mtree->set.gid = mtree->set.gid_list->m_entry->gid;
 		archive_string_sprintf(&setstr, " gid=%jd",
 		    (intmax_t)mtree->set.gid);
 	}
 	if ((keys & effkeys & F_MODE) != 0) {
-		mtree->set.mode = archive_entry_mode(entry) & 07777;
-		archive_string_sprintf(&setstr, " mode=%o", mtree->set.mode);
+		mtree->set.mode = mtree->set.mode_list->m_entry->mode;
+		archive_string_sprintf(&setstr, " mode=%o",
+		    (unsigned int)mtree->set.mode);
 	}
 	if ((keys & effkeys & F_FLAGS) != 0) {
-		if ((name = archive_entry_fflags_text(entry)) != NULL) {
+		name = mtree->set.flags_list->m_entry->fflags_text;
+		if (name != NULL) {
 			archive_strcat(&setstr, " flags=");
 			mtree_quote(&setstr, name);
-			archive_entry_fflags(entry, &mtree->set.fflags_set,
-			    &mtree->set.fflags_clear);
-		} else if ((oldkeys & F_FLAGS) != 0)
-			archive_strcat(&unsetstr, " flags");
-		else
+			mtree->set.fflags_set =
+			    mtree->set.flags_list->m_entry->fflags_set;
+			mtree->set.fflags_clear =
+			    mtree->set.flags_list->m_entry->fflags_clear;
+		} else {
 			keys &= ~F_FLAGS;
+			if ((oldkeys & F_FLAGS) != 0)
+				archive_strcat(&unsetstr, " flags");
+		}
 	}
 	if (unsetstr.length > 0)
 		archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s);
@@ -491,38 +579,196 @@
 	archive_string_free(&setstr);
 	mtree->set.keys = keys;
 	mtree->set.processed = 1;
-	/* On directory-only mode, it is only once to write /set keyword. */
-	if (mtree->dironly)
-		mtree->set.output = 0;
+
+	free_attr_count(&mtree->set.uid_list);
+	free_attr_count(&mtree->set.gid_list);
+	free_attr_count(&mtree->set.mode_list);
+	free_attr_count(&mtree->set.flags_list);
+}
+
+static struct attr_counter *
+new_attr_count(struct mtree_entry *me, struct attr_counter *prev)
+{
+	struct attr_counter *ac;
+
+	ac = malloc(sizeof(*ac));
+	if (ac != NULL) {
+		ac->prev = prev;
+		ac->next = NULL;
+		ac->count = 1;
+		ac->m_entry = me;
+	}
+	return (ac);
+}
+
+static void
+free_attr_count(struct attr_counter **top)
+{
+	struct attr_counter *ac, *tac;
+
+	if (*top == NULL)
+		return;
+	ac = *top;
+        while (ac != NULL) {
+		tac = ac->next;
+		free(ac);
+		ac = tac;
+	}
+	*top = NULL;
 }
 
 static int
-get_keys(struct mtree_writer *mtree, struct archive_entry *entry)
+inc_attr_count(struct attr_counter **top, struct attr_counter *ac,
+    struct attr_counter *last, struct mtree_entry *me)
+{
+	struct attr_counter *pac;
+
+	if (ac != NULL) {
+		ac->count++;
+		if (*top == ac || ac->prev->count >= ac->count)
+			return (0);
+		for (pac = ac->prev; pac; pac = pac->prev) {
+			if (pac->count >= ac->count)
+				break;
+		}
+		ac->prev->next = ac->next;
+		if (ac->next != NULL)
+			ac->next->prev = ac->prev;
+		if (pac != NULL) {
+			ac->prev = pac;
+			ac->next = pac->next;
+			pac->next = ac;
+			if (ac->next != NULL)
+				ac->next->prev = ac;
+		} else {
+			ac->prev = NULL;
+			ac->next = *top;
+			*top = ac;
+			ac->next->prev = ac;
+		}
+	} else {
+		ac = new_attr_count(me, last);
+		if (ac == NULL)
+			return (-1);
+		last->next = ac;
+	}
+	return (0);
+}
+
+static int
+collect_set_values(struct mtree_writer *mtree, struct mtree_entry *me)
+{
+	int keys = mtree->keys;
+	struct attr_counter *ac, *last;
+
+	if (keys & (F_UNAME | F_UID)) {
+		if (mtree->set.uid_list == NULL) {
+			mtree->set.uid_list = new_attr_count(me, NULL);
+			if (mtree->set.uid_list == NULL)
+				return (-1);
+		} else {
+			last = NULL;
+			for (ac = mtree->set.uid_list; ac; ac = ac->next) {
+				if (ac->m_entry->uid == me->uid)
+					break;
+				last = ac;
+			}
+			if (inc_attr_count(
+			    &mtree->set.uid_list, ac, last, me) < 0)
+				return (-1);
+		}
+	}
+	if (keys & (F_GNAME | F_GID)) {
+		if (mtree->set.gid_list == NULL) {
+			mtree->set.gid_list = new_attr_count(me, NULL);
+			if (mtree->set.gid_list == NULL)
+				return (-1);
+		} else {
+			last = NULL;
+			for (ac = mtree->set.gid_list; ac; ac = ac->next) {
+				if (ac->m_entry->gid == me->gid)
+					break;
+				last = ac;
+			}
+			if (inc_attr_count(
+			    &mtree->set.gid_list, ac, last, me) < 0)
+				return (-1);
+		}
+	}
+	if (keys & F_MODE) {
+		if (mtree->set.mode_list == NULL) {
+			mtree->set.mode_list = new_attr_count(me, NULL);
+			if (mtree->set.mode_list == NULL)
+				return (-1);
+		} else {
+			last = NULL;
+			for (ac = mtree->set.mode_list; ac; ac = ac->next) {
+				if (ac->m_entry->mode == me->mode)
+					break;
+				last = ac;
+			}
+			if (inc_attr_count(
+			    &mtree->set.mode_list, ac, last, me) < 0)
+				return (-1);
+		}
+	}
+	if (keys & F_FLAGS) {
+		if (mtree->set.flags_list == NULL) {
+			mtree->set.flags_list = new_attr_count(me, NULL);
+			if (mtree->set.flags_list == NULL)
+				return (-1);
+		} else {
+			last = NULL;
+			for (ac = mtree->set.flags_list; ac; ac = ac->next) {
+				if (ac->m_entry->fflags_set == me->fflags_set &&
+				    ac->m_entry->fflags_clear == me->fflags_clear)
+					break;
+				last = ac;
+			}
+			if (inc_attr_count(
+			    &mtree->set.flags_list, ac, last, me) < 0)
+				return (-1);
+		}
+	}
+
+	/*
+	 * Save a entry.
+	 */
+	me->next = NULL;
+	*mtree->set.me_last = me;
+	mtree->set.me_last = &me->next;
+	return (0);
+}
+
+static int
+get_keys(struct mtree_writer *mtree, struct mtree_entry *me)
 {
 	int keys;
 
 	keys = mtree->keys;
+
+	/*
+	 * If a keyword has been set by /set, we do not need to
+	 * output it.
+	 */
 	if (mtree->set.keys == 0)
-		return (keys);
+		return (keys);/* /set is not used. */
+
 	if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 &&
-	     mtree->set.gid == archive_entry_gid(entry))
+	     mtree->set.gid == me->gid)
 		keys &= ~(F_GNAME | F_GID);
 	if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 &&
-	     mtree->set.uid == archive_entry_uid(entry))
+	     mtree->set.uid == me->uid)
 		keys &= ~(F_UNAME | F_UID);
 	if (mtree->set.keys & F_FLAGS) {
-		unsigned long set, clear;
-
-		archive_entry_fflags(entry, &set, &clear);
-		if (mtree->set.fflags_set == set &&
-		    mtree->set.fflags_clear == clear)
+		if (mtree->set.fflags_set == me->fflags_set &&
+		    mtree->set.fflags_clear == me->fflags_clear)
 			keys &= ~F_FLAGS;
 	}
-	if ((mtree->set.keys & F_MODE) != 0 &&
-	     mtree->set.mode == (archive_entry_mode(entry) & 07777))
+	if ((mtree->set.keys & F_MODE) != 0 && mtree->set.mode == me->mode)
 		keys &= ~F_MODE;
 
-	switch (archive_entry_filetype(entry)) {
+	switch (me->filetype) {
 	case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR:
 	case AE_IFBLK: case AE_IFIFO:
 		break;
@@ -542,171 +788,145 @@
 	return (keys);
 }
 
+static struct mtree_entry *
+new_mtree_entry(struct archive_entry *entry)
+{
+	struct mtree_entry *me;
+	const char *s;
+
+	me = calloc(1, sizeof(*me));
+	if (me == NULL)
+		return (NULL);
+	me->pathname = strdup(archive_entry_pathname(entry));
+	if ((s = archive_entry_symlink(entry)) != NULL)
+		me->symlink = strdup(s);
+	else
+		me->symlink = NULL;
+	me->nlink = archive_entry_nlink(entry);
+	me->filetype = archive_entry_filetype(entry);
+	me->mode = archive_entry_mode(entry) & 07777;
+	me->uid = archive_entry_uid(entry);
+	me->gid = archive_entry_gid(entry);
+	if ((s = archive_entry_uname(entry)) != NULL)
+		me->uname = strdup(s);
+	else
+		me->uname = NULL;
+	if ((s = archive_entry_gname(entry)) != NULL)
+		me->gname = strdup(s);
+	else
+		me->gname = NULL;
+	if ((s = archive_entry_fflags_text(entry)) != NULL)
+		me->fflags_text = strdup(s);
+	else
+		me->fflags_text = NULL;
+	archive_entry_fflags(entry, &me->fflags_set, &me->fflags_clear);
+	me->mtime = archive_entry_mtime(entry);
+	me->mtime_nsec = archive_entry_mtime_nsec(entry);
+	me->rdevmajor =	archive_entry_rdevmajor(entry);
+	me->rdevminor = archive_entry_rdevminor(entry);
+	me->size = archive_entry_size(entry);
+	me->compute_sum = 0;
+
+	return (me);
+}
+
+static void
+free_mtree_entry(struct mtree_entry *me)
+{
+	free(me->pathname);
+	free(me->symlink);
+	free(me->uname);
+	free(me->gname);
+	free(me->fflags_text);
+	free(me);
+}
+
 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->keys & SET_KEYS) == 0)
+			mtree->set.output = 0;/* Disalbed. */
 	}
-	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
+	if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR)
+		return (ARCHIVE_OK);
+
+	mtree->mtree_entry = new_mtree_entry(entry);
+	if (mtree->mtree_entry == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate mtree entry");
+		return (ARCHIVE_FATAL);
+	}
+
+	mtree->compute_sum = 0;
+
+	/* If current file is not a regular file, we do not have to
+	 * compute the sum of its content. */ 
+	if (archive_entry_filetype(entry) != AE_IFREG)
+		return (ARCHIVE_OK);
+		
+	/* Initialize a bunch of sum check context. */
+	sum_init(mtree);
 
 	return (ARCHIVE_OK);
 }
 
-#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) || \
-    defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) || \
-    defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512)
-static void
-strappend_bin(struct archive_string *s, const unsigned char *bin, int n)
-{
-	static const char hex[] = "0123456789abcdef";
-	int i;
-
-	for (i = 0; i < n; i++) {
-		archive_strappend_char(s, hex[bin[i] >> 4]);
-		archive_strappend_char(s, hex[bin[i] & 0x0f]);
-	}
-}
-#endif
-
 static int
-archive_write_mtree_finish_entry(struct archive_write *a)
+write_entry(struct archive_write *a, struct mtree_entry *me)
 {
 	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);
+	archive_string_empty(&mtree->ebuf);
+	str = (mtree->indent)? &mtree->ebuf : &mtree->buf;
+	mtree_quote(str, me->pathname);
+	keys = get_keys(mtree, me);
+	if ((keys & F_NLINK) != 0 &&
+	    me->nlink != 1 && me->filetype != AE_IFDIR)
+		archive_string_sprintf(str, " nlink=%u", me->nlink);
+
+	if ((keys & F_GNAME) != 0 && me->gname != NULL) {
+		archive_strcat(str, " gname=");
+		mtree_quote(str, me->gname);
 	}
-	mtree->entry = NULL;
-
-	if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR) {
-		archive_entry_free(entry);
-		return (ARCHIVE_OK);
+	if ((keys & F_UNAME) != 0 && me->uname != NULL) {
+		archive_strcat(str, " uname=");
+		mtree_quote(str, me->uname);
 	}
-
-	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_FLAGS) != 0) {
+		if (me->fflags_text != NULL) {
+			archive_strcat(str, " flags=");
+			mtree_quote(str, me->fflags_text);
+		} else if (mtree->set.processed &&
+		    (mtree->set.keys & F_FLAGS) != 0)
+			/* Overwrite the global parameter. */
+			archive_strcat(str, " flags=none");
 	}
 	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));
+		    (intmax_t)me->mtime, (intmax_t)me->mtime_nsec);
 	if ((keys & F_MODE) != 0)
-		archive_string_sprintf(str, " mode=%o",
-		    archive_entry_mode(entry) & 07777);
+		archive_string_sprintf(str, " mode=%o", (unsigned int)me->mode);
 	if ((keys & F_GID) != 0)
-		archive_string_sprintf(str, " gid=%jd",
-		    (intmax_t)archive_entry_gid(entry));
+		archive_string_sprintf(str, " gid=%jd", (intmax_t)me->gid);
 	if ((keys & F_UID) != 0)
-		archive_string_sprintf(str, " uid=%jd",
-		    (intmax_t)archive_entry_uid(entry));
+		archive_string_sprintf(str, " uid=%jd", (intmax_t)me->uid);
 
-	switch (archive_entry_filetype(entry)) {
+	switch (me->filetype) {
 	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));
+			mtree_quote(str, me->symlink);
 		}
 		break;
 	case AE_IFSOCK:
@@ -718,9 +938,9 @@
 			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));
+			    " device=native,%ju,%ju",
+			    (uintmax_t)me->rdevmajor,
+			    (uintmax_t)me->rdevminor);
 		}
 		break;
 	case AE_IFBLK:
@@ -728,9 +948,9 @@
 			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));
+			    " device=native,%ju,%ju",
+			    (uintmax_t)me->rdevmajor,
+			    (uintmax_t)me->rdevminor);
 		}
 		break;
 	case AE_IFDIR:
@@ -747,96 +967,110 @@
 			archive_strcat(str, " type=file");
 		if ((keys & F_SIZE) != 0)
 			archive_string_sprintf(str, " size=%jd",
-			    (intmax_t)archive_entry_size(entry));
+			    (intmax_t)me->size);
 		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);
-	}
-#ifdef ARCHIVE_HAS_MD5
-	if (mtree->compute_sum & F_MD5) {
-		unsigned char buf[16];
+	/* Write a bunch of sum. */
+	if (me->filetype == AE_IFREG)
+		sum_write(str, me);
 
-		archive_md5_final(&mtree->md5ctx, buf);
-		archive_strcat(str, " md5digest=");
-		strappend_bin(str, buf, sizeof(buf));
-	}
-#endif
-#ifdef ARCHIVE_HAS_RMD160
-	if (mtree->compute_sum & F_RMD160) {
-		unsigned char buf[20];
-
-		archive_rmd160_final(&mtree->rmd160ctx, buf);
-		archive_strcat(str, " rmd160digest=");
-		strappend_bin(str, buf, sizeof(buf));
-	}
-#endif
-#ifdef ARCHIVE_HAS_SHA1
-	if (mtree->compute_sum & F_SHA1) {
-		unsigned char buf[20];
-
-		archive_sha1_final(&mtree->sha1ctx, buf);
-		archive_strcat(str, " sha1digest=");
-		strappend_bin(str, buf, sizeof(buf));
-	}
-#endif
-#ifdef ARCHIVE_HAS_SHA256
-	if (mtree->compute_sum & F_SHA256) {
-		unsigned char buf[32];
-
-		archive_sha256_final(&mtree->sha256ctx, buf);
-		archive_strcat(str, " sha256digest=");
-		strappend_bin(str, buf, sizeof(buf));
-	}
-#endif
-#ifdef ARCHIVE_HAS_SHA384
-	if (mtree->compute_sum & F_SHA384) {
-		unsigned char buf[48];
-
-		archive_sha384_final(&mtree->sha384ctx, buf);
-		archive_strcat(str, " sha384digest=");
-		strappend_bin(str, buf, sizeof(buf));
-	}
-#endif
-#ifdef ARCHIVE_HAS_SHA512
-	if (mtree->compute_sum & F_SHA512) {
-		unsigned char buf[64];
-
-		archive_sha512_final(&mtree->sha512ctx, buf);
-		archive_strcat(str, " sha512digest=");
-		strappend_bin(str, buf, sizeof(buf));
-	}
-#endif
 	archive_strcat(str, "\n");
 	if (mtree->indent)
 		mtree_indent(mtree);
 
-	archive_entry_free(entry);
-
 	if (mtree->buf.length > 32768) {
-		ret = (a->compressor.write)(a, mtree->buf.s, mtree->buf.length);
+		ret = __archive_write_output(a, mtree->buf.s, mtree->buf.length);
 		archive_string_empty(&mtree->buf);
 	} else
 		ret = ARCHIVE_OK;
+	return (ret);
+}
 
+/*
+ * Write mtree entries saved at collect_set_values() function.
+ */
+static int
+write_mtree_entries(struct archive_write *a)
+{
+	struct mtree_writer *mtree = a->format_data;
+	struct mtree_entry *me, *tme;
+	int ret;
+
+	for (me = mtree->set.me_first; me; me = me->next) {
+		ret = write_entry(a, me);
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	me = mtree->set.me_first;
+	while (me != NULL) {
+		tme = me->next;
+		free_mtree_entry(me);
+		me = tme;
+	}
+	mtree->set.me_first = NULL;
+	mtree->set.me_last = &mtree->set.me_first;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_mtree_finish_entry(struct archive_write *a)
+{
+	struct mtree_writer *mtree = a->format_data;
+	struct mtree_entry *me;
+	int ret;
+
+	if ((me = mtree->mtree_entry) == NULL)
+		return (ARCHIVE_OK);
+	mtree->mtree_entry = NULL;
+
+	if (me->filetype == AE_IFREG)
+		sum_final(mtree, me);
+
+	if (mtree->set.output) {
+		if (!mtree->dironly) {
+			if (archive_strlen(&mtree->set.parent) == 0)
+				parent_dir_changed(&mtree->set.parent, me);
+			if (parent_dir_changed(&mtree->set.parent, me)) {
+				/* Write /set keyword */
+				write_global(mtree);
+				/* Write entries saved by
+				 * collect_set_values() function. */
+				ret = write_mtree_entries(a);
+				if (ret != ARCHIVE_OK)
+					return (ARCHIVE_FATAL);
+			}
+		}
+		/* Tabulate uid,gid,mode and fflags of a entry
+		 * in order to be used for /set. and, at this time
+		 * we do not write a entry.  */
+		collect_set_values(mtree, me);
+		return (ARCHIVE_OK);
+	} else {
+		/* Write the current entry and free it. */
+		ret = write_entry(a, me);
+		free_mtree_entry(me);
+	}
 	return (ret == ARCHIVE_OK ? ret : ARCHIVE_FATAL);
 }
 
 static int
-archive_write_mtree_finish(struct archive_write *a)
+archive_write_mtree_close(struct archive_write *a)
 {
 	struct mtree_writer *mtree= a->format_data;
+	int ret;
+
+	if (mtree->set.output && mtree->set.me_first != NULL) {
+		write_global(mtree);
+		ret = write_mtree_entries(a);
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
 
 	archive_write_set_bytes_in_last_block(&a->archive, 1);
 
-	return (a->compressor.write)(a, mtree->buf.s, mtree->buf.length);
+	return __archive_write_output(a, mtree->buf.s, mtree->buf.length);
 }
 
 static ssize_t
@@ -846,59 +1080,41 @@
 
 	if (n > mtree->entry_bytes_remaining)
 		n = mtree->entry_bytes_remaining;
-	if (mtree->dironly)
-		/* We don't need compute a regular file sum */
+	mtree->entry_bytes_remaining -= n;
+
+	/* We don't need to compute a regular file sum */
+	if (mtree->mtree_entry == NULL)
 		return (n);
-	if (mtree->compute_sum & F_CKSUM) {
-		/*
-		 * Compute a POSIX 1003.2 checksum
-		 */
-		const unsigned char *p;
-		size_t nn;
 
-		for (nn = n, p = buff; nn--; ++p)
-			COMPUTE_CRC(mtree->crc, *p);
-		mtree->crc_len += n;
-	}
-#ifdef ARCHIVE_HAS_MD5
-	if (mtree->compute_sum & F_MD5)
-		archive_md5_update(&mtree->md5ctx, buff, n);
-#endif
-#ifdef ARCHIVE_HAS_RMD160
-	if (mtree->compute_sum & F_RMD160)
-		archive_rmd160_update(&mtree->rmd160ctx, buff, n);
-#endif
-#ifdef ARCHIVE_HAS_SHA1
-	if (mtree->compute_sum & F_SHA1)
-		archive_sha1_update(&mtree->sha1ctx, buff, n);
-#endif
-#ifdef ARCHIVE_HAS_SHA256
-	if (mtree->compute_sum & F_SHA256)
-		archive_sha256_update(&mtree->sha256ctx, buff, n);
-#endif
-#ifdef ARCHIVE_HAS_SHA384
-	if (mtree->compute_sum & F_SHA384)
-		archive_sha384_update(&mtree->sha384ctx, buff, n);
-#endif
-#ifdef ARCHIVE_HAS_SHA512
-	if (mtree->compute_sum & F_SHA512)
-		archive_sha512_update(&mtree->sha512ctx, buff, n);
-#endif
+	if (mtree->mtree_entry->filetype == AE_IFREG)
+		sum_update(mtree, buff, n);
+
 	return (n);
 }
 
 static int
-archive_write_mtree_destroy(struct archive_write *a)
+archive_write_mtree_free(struct archive_write *a)
 {
 	struct mtree_writer *mtree= a->format_data;
+	struct mtree_entry *me, *tme;
 
 	if (mtree == NULL)
 		return (ARCHIVE_OK);
 
-	archive_entry_free(mtree->entry);
+	/* Make sure we dot not leave any entries. */
+	me = mtree->set.me_first;
+	while (me != NULL) {
+		tme = me->next;
+		free_mtree_entry(me);
+		me = tme;
+	}
 	archive_string_free(&mtree->ebuf);
 	archive_string_free(&mtree->buf);
 	archive_string_free(&mtree->set.parent);
+	free_attr_count(&mtree->set.uid_list);
+	free_attr_count(&mtree->set.gid_list);
+	free_attr_count(&mtree->set.mode_list);
+	free_attr_count(&mtree->set.flags_list);
 	free(mtree);
 	a->format_data = NULL;
 	return (ARCHIVE_OK);
@@ -1006,6 +1222,9 @@
 		return (ARCHIVE_OK);
 	}
 
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
 	return (ARCHIVE_WARN);
 }
 
@@ -1015,16 +1234,19 @@
 	struct archive_write *a = (struct archive_write *)_a;
 	struct mtree_writer *mtree;
 
-	if (a->format_destroy != NULL)
-		(a->format_destroy)(a);
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_mtree");
 
-	if ((mtree = malloc(sizeof(*mtree))) == NULL) {
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	if ((mtree = calloc(1, sizeof(*mtree))) == NULL) {
 		archive_set_error(&a->archive, ENOMEM,
 		    "Can't allocate mtree data");
 		return (ARCHIVE_FATAL);
 	}
 
-	mtree->entry = NULL;
+	mtree->mtree_entry = NULL;
 	mtree->first = 1;
 	memset(&(mtree->set), 0, sizeof(mtree->set));
 	archive_string_init(&mtree->set.parent);
@@ -1033,14 +1255,14 @@
 	mtree->indent = 0;
 	archive_string_init(&mtree->ebuf);
 	archive_string_init(&mtree->buf);
+	mtree->set.me_first = NULL;
+	mtree->set.me_last = &mtree->set.me_first;
 	a->format_data = mtree;
-	a->format_destroy = archive_write_mtree_destroy;
-
-	a->pad_uncompressed = 0;
+	a->format_free = archive_write_mtree_free;
 	a->format_name = "mtree";
 	a->format_options = archive_write_mtree_options;
 	a->format_write_header = archive_write_mtree_header;
-	a->format_finish = archive_write_mtree_finish;
+	a->format_close = archive_write_mtree_close;
 	a->format_write_data = archive_write_mtree_data;
 	a->format_finish_entry = archive_write_mtree_finish_entry;
 	a->archive.archive_format = ARCHIVE_FORMAT_MTREE;
@@ -1048,3 +1270,202 @@
 
 	return (ARCHIVE_OK);
 }
+
+static void
+sum_init(struct mtree_writer *mtree)
+{
+	if (mtree->keys & F_CKSUM) {
+		mtree->compute_sum |= F_CKSUM;
+		mtree->crc = 0;
+		mtree->crc_len = 0;
+	}
+#ifdef ARCHIVE_HAS_MD5
+	if (mtree->keys & F_MD5) {
+		if (archive_md5_init(&mtree->md5ctx) == ARCHIVE_OK)
+			mtree->compute_sum |= F_MD5;
+		else
+			mtree->keys &= ~F_MD5;/* Not supported. */
+	}
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	if (mtree->keys & F_RMD160) {
+		if (archive_rmd160_init(&mtree->rmd160ctx) == ARCHIVE_OK)
+			mtree->compute_sum |= F_RMD160;
+		else
+			mtree->keys &= ~F_RMD160;/* Not supported. */
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	if (mtree->keys & F_SHA1) {
+		if (archive_sha1_init(&mtree->sha1ctx) == ARCHIVE_OK)
+			mtree->compute_sum |= F_SHA1;
+		else
+			mtree->keys &= ~F_SHA1;/* Not supported. */
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	if (mtree->keys & F_SHA256) {
+		if (archive_sha256_init(&mtree->sha256ctx) == ARCHIVE_OK)
+			mtree->compute_sum |= F_SHA256;
+		else
+			mtree->keys &= ~F_SHA256;/* Not supported. */
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	if (mtree->keys & F_SHA384) {
+		if (archive_sha384_init(&mtree->sha384ctx) == ARCHIVE_OK)
+			mtree->compute_sum |= F_SHA384;
+		else
+			mtree->keys &= ~F_SHA384;/* Not supported. */
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	if (mtree->keys & F_SHA512) {
+		if (archive_sha512_init(&mtree->sha512ctx) == ARCHIVE_OK)
+			mtree->compute_sum |= F_SHA512;
+		else
+			mtree->keys &= ~F_SHA512;/* Not supported. */
+	}
+#endif
+}
+
+static void
+sum_update(struct mtree_writer *mtree, const void *buff, size_t n)
+{
+	if (mtree->compute_sum & F_CKSUM) {
+		/*
+		 * Compute a POSIX 1003.2 checksum
+		 */
+		const unsigned char *p;
+		size_t nn;
+
+		for (nn = n, p = buff; nn--; ++p)
+			COMPUTE_CRC(mtree->crc, *p);
+		mtree->crc_len += n;
+	}
+#ifdef ARCHIVE_HAS_MD5
+	if (mtree->compute_sum & F_MD5)
+		archive_md5_update(&mtree->md5ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	if (mtree->compute_sum & F_RMD160)
+		archive_rmd160_update(&mtree->rmd160ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	if (mtree->compute_sum & F_SHA1)
+		archive_sha1_update(&mtree->sha1ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	if (mtree->compute_sum & F_SHA256)
+		archive_sha256_update(&mtree->sha256ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	if (mtree->compute_sum & F_SHA384)
+		archive_sha384_update(&mtree->sha384ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	if (mtree->compute_sum & F_SHA512)
+		archive_sha512_update(&mtree->sha512ctx, buff, n);
+#endif
+}
+
+static void
+sum_final(struct mtree_writer *mtree, struct mtree_entry *me)
+{
+
+	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);
+		me->crc = ~mtree->crc;
+	}
+#ifdef ARCHIVE_HAS_MD5
+	if (mtree->compute_sum & F_MD5)
+		archive_md5_final(&mtree->md5ctx, me->buf_md5);
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	if (mtree->compute_sum & F_RMD160)
+		archive_rmd160_final(&mtree->rmd160ctx, me->buf_rmd160);
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	if (mtree->compute_sum & F_SHA1)
+		archive_sha1_final(&mtree->sha1ctx, me->buf_sha1);
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	if (mtree->compute_sum & F_SHA256)
+		archive_sha256_final(&mtree->sha256ctx, me->buf_sha256);
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	if (mtree->compute_sum & F_SHA384)
+		archive_sha384_final(&mtree->sha384ctx, me->buf_sha384);
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	if (mtree->compute_sum & F_SHA512)
+		archive_sha512_final(&mtree->sha512ctx, me->buf_sha512);
+#endif
+	/* Save what types of sum are computed. */
+	me->compute_sum = mtree->compute_sum;
+}
+
+#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) || \
+    defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) || \
+    defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512)
+static void
+strappend_bin(struct archive_string *s, const unsigned char *bin, int n)
+{
+	static const char hex[] = "0123456789abcdef";
+	int i;
+
+	for (i = 0; i < n; i++) {
+		archive_strappend_char(s, hex[bin[i] >> 4]);
+		archive_strappend_char(s, hex[bin[i] & 0x0f]);
+	}
+}
+#endif
+
+static void
+sum_write(struct archive_string *str, struct mtree_entry *me)
+{
+
+	if (me->compute_sum & F_CKSUM) {
+		archive_string_sprintf(str, " cksum=%ju",
+		    (uintmax_t)me->crc);
+	}
+#ifdef ARCHIVE_HAS_MD5
+	if (me->compute_sum & F_MD5) {
+		archive_strcat(str, " md5digest=");
+		strappend_bin(str, me->buf_md5, sizeof(me->buf_md5));
+	}
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	if (me->compute_sum & F_RMD160) {
+		archive_strcat(str, " rmd160digest=");
+		strappend_bin(str, me->buf_rmd160, sizeof(me->buf_rmd160));
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	if (me->compute_sum & F_SHA1) {
+		archive_strcat(str, " sha1digest=");
+		strappend_bin(str, me->buf_sha1, sizeof(me->buf_sha1));
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	if (me->compute_sum & F_SHA256) {
+		archive_strcat(str, " sha256digest=");
+		strappend_bin(str, me->buf_sha256, sizeof(me->buf_sha256));
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	if (me->compute_sum & F_SHA384) {
+		archive_strcat(str, " sha384digest=");
+		strappend_bin(str, me->buf_sha384, sizeof(me->buf_sha384));
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	if (me->compute_sum & F_SHA512) {
+		archive_strcat(str, " sha512digest=");
+		strappend_bin(str, me->buf_sha512, sizeof(me->buf_sha512));
+	}
+#endif
+}
diff -r b8e0795316a9 -r a6a608a13e37 head/contrib/libarchive/libarchive/archive_write_set_format_pax.c
--- a/head/contrib/libarchive/libarchive/archive_write_set_format_pax.c	Fri Mar 02 16:52:45 2012 +0200
+++ b/head/contrib/libarchive/libarchive/archive_write_set_format_pax.c	Fri Mar 02 16:54:40 2012 +0200
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,7 +25,7 @@
  */
 
 #include "archive_platform.h"
-__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format_pax.c 228763 2011-12-21 11:13:29Z mm $");
+__FBSDID("$FreeBSD: head/contrib/libarchive/libarchive/archive_write_set_format_pax.c 232153 2012-02-25 10:58:02Z mm $");
 
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
@@ -38,13 +39,28 @@
 
 #include "archive.h"
 #include "archive_entry.h"
+#include "archive_entry_locale.h"
 #include "archive_private.h"
 #include "archive_write_private.h"
 
+struct sparse_block {
+	struct sparse_block	*next;
+	int		is_hole;
+	uint64_t	offset;
+	uint64_t	remaining;
+};
+
 struct pax {
 	uint64_t	entry_bytes_remaining;
 	uint64_t	entry_padding;
+	struct archive_string	l_url_encoded_name;
 	struct archive_string	pax_header;
+	struct archive_string	sparse_map;
+	size_t			sparse_map_padding;
+	struct sparse_block	*sparse_list;
+	struct sparse_block	*sparse_tail;
+	struct archive_string_conv *sconv_utf8;
+	int			 opt_binary;
 };
 
 static void		 add_pax_attr(struct archive_string *, const char *key,
@@ -54,23 +70,25 @@
 static void		 add_pax_attr_time(struct archive_string *,
 			     const char *key, int64_t sec,
 			     unsigned long nanos);
-static void		 add_pax_attr_w(struct archive_string *,
-			     const char *key, const wchar_t *wvalue);
 static ssize_t		 archive_write_pax_data(struct archive_write *,
 			     const void *, size_t);
-static int		 archive_write_pax_finish(struct archive_write *);
-static int		 archive_write_pax_destroy(struct archive_write *);
+static int		 archive_write_pax_close(struct archive_write *);
+static int		 archive_write_pax_free(struct archive_write *);
 static int		 archive_write_pax_finish_entry(struct archive_write *);
 static int		 archive_write_pax_header(struct archive_write *,
 			     struct archive_entry *);
+static int		 archive_write_pax_options(struct archive_write *,
+			     const char *, const char *);
 static char		*base64_encode(const char *src, size_t len);
+static char		*build_gnu_sparse_name(char *dest, const char *src);
 static char		*build_pax_attribute_name(char *dest, const char *src);
 static char		*build_ustar_entry_name(char *dest, const char *src,
 			     size_t src_length, const char *insert);
 static char		*format_int(char *dest, int64_t);
-static int		 has_non_ASCII(const wchar_t *);
+static int		 has_non_ASCII(const char *);
+static void		 sparse_list_clear(struct pax *);
+static int		 sparse_list_add(struct pax *, int64_t, int64_t);
 static char		*url_encode(const char *in);
-static int		 write_nulls(struct archive_write *, size_t);
 
 /*
  * Set output format to 'restricted pax' format.
@@ -84,6 +102,10 @@
 {
 	struct archive_write *a = (struct archive_write *)_a;
 	int r;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_pax_restricted");
+
 	r = archive_write_set_format_pax(&a->archive);
 	a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED;
 	a->archive.archive_format_name = "restricted POSIX pax interchange";
@@ -99,29 +121,81 @@
 	struct archive_write *a = (struct archive_write *)_a;
 	struct pax *pax;
 
-	if (a->format_destroy != NULL)
-		(a->format_destroy)(a);
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_pax");
+
+	if (a->format_free != NULL)
+		(a->format_free)(a);
 
 	pax = (struct pax *)malloc(sizeof(*pax));
 	if (pax == NULL) {
-		archive_set_error(&a->archive, ENOMEM, "Can't allocate pax data");
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate pax data");
 		return (ARCHIVE_FATAL);
 	}
 	memset(pax, 0, sizeof(*pax));
 	a->format_data = pax;
-
-	a->pad_uncompressed = 1;
 	a->format_name = "pax";
+	a->format_options = archive_write_pax_options;
 	a->format_write_header = archive_write_pax_header;
 	a->format_write_data = archive_write_pax_data;
-	a->format_finish = archive_write_pax_finish;
-	a->format_destroy = archive_write_pax_destroy;
+	a->format_close = archive_write_pax_close;
+	a->format_free = archive_write_pax_free;
 	a->format_finish_entry = archive_write_pax_finish_entry;
 	a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
 	a->archive.archive_format_name = "POSIX pax interchange";
 	return (ARCHIVE_OK);
 }
 
+static int
+archive_write_pax_options(struct archive_write *a, const char *key,
+    const char *val)
+{
+	struct pax *pax = (struct pax *)a->format_data;
+	int ret = ARCHIVE_FAILED;
+
+	if (strcmp(key, "hdrcharset")  == 0) {
+		/*
+		 * The character-set we can use are defined in
+		 * IEEE Std 1003.1-2001
+		 */
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "pax: hdrcharset option needs a character-set name");
+		else if (strcmp(val, "BINARY") == 0 ||
+		    strcmp(val, "binary") == 0) {
+			/*
+			 * Specify binary mode. We will not convert
+			 * filenames, uname and gname to any charsets.
+			 */
+			pax->opt_binary = 1;
+			ret = ARCHIVE_OK;
+		} else if (strcmp(val, "UTF-8") == 0) {
+			/*
+			 * Specify UTF-8 character-set to be used for
+			 * filenames. This is almost the test that
+			 * running platform supports the string conversion.
+			 * Especially libarchive_test needs this trick for
+			 * its test.
+			 */
+			pax->sconv_utf8 = archive_string_conversion_to_charset(
+			    &(a->archive), "UTF-8", 0);
+			if (pax->sconv_utf8 == NULL)
+				ret = ARCHIVE_FATAL;
+			else
+				ret = ARCHIVE_OK;
+		} else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "pax: invalid charset name");
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
 /*
  * Note: This code assumes that 'nanos' has the same sign as 'sec',
  * which implies that sec=-1, nanos=200000000 represents -1.2 seconds
@@ -168,18 +242,17 @@
 static char *
 format_int(char *t, int64_t i)
 {
-	int sign;
+	uint64_t ui;
 
-	if (i < 0) {
-		sign = -1;
-		i = -i;
-	} else
-		sign = 1;
+	if (i < 0) 
+		ui = (i == INT64_MIN) ? (uint64_t)(INT64_MAX) + 1 : (uint64_t)(-i);
+	else
+		ui = i;
 
 	do {
-		*--t = "0123456789"[i % 10];
-	} while (i /= 10);
-	if (sign < 0)
+		*--t = "0123456789"[ui % 10];
+	} while (ui /= 10);
+	if (i < 0)
 		*--t = '-';
 	return (t);
 }
@@ -193,106 +266,6 @@
 	add_pax_attr(as, key, format_int(tmp + sizeof(tmp) - 1, value));
 }
 
-static char *
-utf8_encode(const wchar_t *wval)
-{
-	int utf8len;
-	const wchar_t *wp;
-	unsigned long wc;
-	char *utf8_value, *p;
-
-	utf8len = 0;
-	for (wp = wval; *wp != L'\0'; ) {
-		wc = *wp++;
-
-		if (wc >= 0xd800 && wc <= 0xdbff
-		    && *wp >= 0xdc00 && *wp <= 0xdfff) {
-			/* This is a surrogate pair.  Combine into a
-			 * full Unicode value before encoding into
-			 * UTF-8. */
-			wc = (wc - 0xd800) << 10; /* High 10 bits */
-			wc += (*wp++ - 0xdc00); /* Low 10 bits */
-			wc += 0x10000; /* Skip BMP */
-		}
-		if (wc <= 0x7f)
-			utf8len++;
-		else if (wc <= 0x7ff)
-			utf8len += 2;
-		else if (wc <= 0xffff)
-			utf8len += 3;
-		else if (wc <= 0x1fffff)
-			utf8len += 4;
-		else if (wc <= 0x3ffffff)
-			utf8len += 5;
-		else if (wc <= 0x7fffffff)
-			utf8len += 6;
-		/* Ignore larger values; UTF-8 can't encode them. */
-	}
-
-	utf8_value = (char *)malloc(utf8len + 1);
-	if (utf8_value == NULL) {
-		__archive_errx(1, "Not enough memory for attributes");
-		return (NULL);
-	}
-
-	for (wp = wval, p = utf8_value; *wp != L'\0'; ) {
-		wc = *wp++;
-		if (wc >= 0xd800 && wc <= 0xdbff
-		    && *wp >= 0xdc00 && *wp <= 0xdfff) {
-			/* Combine surrogate pair. */
-			wc = (wc - 0xd800) << 10;
-			wc += *wp++ - 0xdc00 + 0x10000;
-		}
-		if (wc <= 0x7f) {
-			*p++ = (char)wc;
-		} else if (wc <= 0x7ff) {
-			p[0] = 0xc0 | ((wc >> 6) & 0x1f);
-			p[1] = 0x80 | (wc & 0x3f);
-			p += 2;
-		} else if (wc <= 0xffff) {
-			p[0] = 0xe0 | ((wc >> 12) & 0x0f);
-			p[1] = 0x80 | ((wc >> 6) & 0x3f);
-			p[2] = 0x80 | (wc & 0x3f);
-			p += 3;
-		} else if (wc <= 0x1fffff) {
-			p[0] = 0xf0 | ((wc >> 18) & 0x07);
-			p[1] = 0x80 | ((wc >> 12) & 0x3f);
-			p[2] = 0x80 | ((wc >> 6) & 0x3f);
-			p[3] = 0x80 | (wc & 0x3f);
-			p += 4;
-		} else if (wc <= 0x3ffffff) {
-			p[0] = 0xf8 | ((wc >> 24) & 0x03);
-			p[1] = 0x80 | ((wc >> 18) & 0x3f);
-			p[2] = 0x80 | ((wc >> 12) & 0x3f);
-			p[3] = 0x80 | ((wc >> 6) & 0x3f);
-			p[4] = 0x80 | (wc & 0x3f);
-			p += 5;
-		} else if (wc <= 0x7fffffff) {
-			p[0] = 0xfc | ((wc >> 30) & 0x01);
-			p[1] = 0x80 | ((wc >> 24) & 0x3f);
-			p[1] = 0x80 | ((wc >> 18) & 0x3f);
-			p[2] = 0x80 | ((wc >> 12) & 0x3f);
-			p[3] = 0x80 | ((wc >> 6) & 0x3f);
-			p[4] = 0x80 | (wc & 0x3f);
-			p += 6;
-		}
-		/* Ignore larger values; UTF-8 can't encode them. */
-	}
-	*p = '\0';
-
-	return (utf8_value);
-}
-
-static void
-add_pax_attr_w(struct archive_string *as, const char *key, const wchar_t *wval)
-{
-	char *utf8_value = utf8_encode(wval);
-	if (utf8_value == NULL)
-		return;
-	add_pax_attr(as, key, utf8_value);
-	free(utf8_value);
-}
-
 /*
  * Add a key/value attribute to the pax header.  This function handles
  * the length field and various other syntactic requirements.
@@ -342,8 +315,9 @@
 	archive_strappend_char(as, '\n');
 }
 
-static void
-archive_write_pax_header_xattrs(struct pax *pax, struct archive_entry *entry)
+static int
+archive_write_pax_header_xattrs(struct archive_write *a,
+    struct pax *pax, struct archive_entry *entry)
 {
 	struct archive_string s;
 	int i = archive_entry_xattr_reset(entry);
@@ -353,26 +327,24 @@
 		const void *value;
 		char *encoded_value;
 		char *url_encoded_name = NULL, *encoded_name = NULL;
-		wchar_t *wcs_name = NULL;
 		size_t size;
+		int r;
 
 		archive_entry_xattr_next(entry, &name, &value, &size);
-		/* Name is URL-encoded, then converted to wchar_t,
-		 * then UTF-8 encoded. */
 		url_encoded_name = url_encode(name);
 		if (url_encoded_name != NULL) {
-			/* Convert narrow-character to wide-character. */
-			size_t wcs_length = strlen(url_encoded_name);
-			wcs_name = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t));
-			if (wcs_name == NULL)
-				__archive_errx(1, "No memory for xattr conversion");
-			mbstowcs(wcs_name, url_encoded_name, wcs_length);
-			wcs_name[wcs_length] = 0;
+			/* Convert narrow-character to UTF-8. */
+			r = archive_strcpy_in_locale(
+			    &(pax->l_url_encoded_name),
+			    url_encoded_name, pax->sconv_utf8);
 			free(url_encoded_name); /* Done with this. */
-		}
-		if (wcs_name != NULL) {
-			encoded_name = utf8_encode(wcs_name);
-			free(wcs_name); /* Done with wchar_t name. */
+			if (r == 0)
+				encoded_name = pax->l_url_encoded_name.s;
+			else if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for Linkname");
+				return (ARCHIVE_FATAL);
+			}
 		}
 
 		encoded_value = base64_encode((const char *)value, size);
@@ -384,9 +356,99 @@
 			add_pax_attr(&(pax->pax_header), s.s, encoded_value);
 			archive_string_free(&s);
 		}
-		free(encoded_name);
 		free(encoded_value);
 	}
+	return (ARCHIVE_OK);
+}
+
+static int
+get_entry_hardlink(struct archive_write *a, struct archive_entry *entry,
+    const char **name, size_t *length, struct archive_string_conv *sc)
+{
+	int r;
+	
+	r = archive_entry_hardlink_l(entry, name, length, sc);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+get_entry_pathname(struct archive_write *a, struct archive_entry *entry,
+    const char **name, size_t *length, struct archive_string_conv *sc)
+{
+	int r;
+
+	r = archive_entry_pathname_l(entry, name, length, sc);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+get_entry_uname(struct archive_write *a, struct archive_entry *entry,
+    const char **name, size_t *length, struct archive_string_conv *sc)
+{
+	int r;
+
+	r = archive_entry_uname_l(entry, name, length, sc);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Uname");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+get_entry_gname(struct archive_write *a, struct archive_entry *entry,
+    const char **name, size_t *length, struct archive_string_conv *sc)
+{
+	int r;
+
+	r = archive_entry_gname_l(entry, name, length, sc);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Gname");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+get_entry_symlink(struct archive_write *a, struct archive_entry *entry,
+    const char **name, size_t *length, struct archive_string_conv *sc)
+{
+	int r;
+
+	r = archive_entry_symlink_l(entry, name, length, sc);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
 }
 
 /*
@@ -394,6 +456,8 @@
  * archive_entry so that clients can specify them.  Also, consider
  * adding generic key/value tags so clients can add arbitrary
  * key/value data.
+ *
+ * TODO: Break up this 700-line function!!!!  Yowza!
  */
 static int
 archive_write_pax_header(struct archive_write *a,
@@ -401,28 +465,72 @@
 {
 	struct archive_entry *entry_main;
 	const char *p;
-	char *t;
-	const wchar_t *wp;
 	const char *suffix;
 	int need_extension, r, ret;
+	int sparse_count;
+	uint64_t sparse_total, real_size;
 	struct pax *pax;
-	const char *hdrcharset = NULL;
 	const char *hardlink;
 	const char *path = NULL, *linkpath = NULL;
 	const char *uname = NULL, *gname = NULL;
-	const wchar_t *path_w = NULL, *linkpath_w = NULL;
-	const wchar_t *uname_w = NULL, *gname_w = NULL;
+	const void *mac_metadata;
+	size_t mac_metadata_size;
+	struct archive_string_conv *sconv;
+	size_t hardlink_length, path_length, linkpath_length;
+	size_t uname_length, gname_length;
 
 	char paxbuff[512];
 	char ustarbuff[512];
 	char ustar_entry_name[256];
 	char pax_entry_name[256];
+	char gnu_sparse_name[256];
+	struct archive_string entry_name;
 
 	ret = ARCHIVE_OK;
 	need_extension = 0;
 	pax = (struct pax *)a->format_data;
 
-	hardlink = archive_entry_hardlink(entry_original);
+	/* Sanity check. */
+	if (archive_entry_pathname(entry_original) == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			  "Can't record entry in tar file without pathname");
+		return (ARCHIVE_FAILED);
+	}
+
+	/*
+	 * Choose a header encoding.
+	 */
+	if (pax->opt_binary)
+		sconv = NULL;/* Binary mode. */
+	else {
+		/* Header encoding is UTF-8. */
+		if (pax->sconv_utf8 == NULL) {
+			/* Initialize the string conversion object
+			 * we must need */
+			pax->sconv_utf8 = archive_string_conversion_to_charset(
+			    &(a->archive), "UTF-8", 1);
+			if (pax->sconv_utf8 == NULL)
+				/* Couldn't allocate memory */
+				return (ARCHIVE_FAILED);
+		}
+		sconv = pax->sconv_utf8;
+	}
+
+	r = get_entry_hardlink(a, entry_original, &hardlink,
+	    &hardlink_length, sconv);
+	if (r == ARCHIVE_FATAL)
+		return (r);
+	else if (r != ARCHIVE_OK) {
+		r = get_entry_hardlink(a, entry_original, &hardlink,
+		    &hardlink_length, NULL);
+		if (r == ARCHIVE_FATAL)
+			return (r);
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate linkname '%s' to %s", hardlink,
+		    archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+		sconv = NULL;/* The header charset switches to binary mode. */
+	}
 
 	/* Make sure this is a type of entry that we can handle here */
 	if (hardlink == NULL) {
@@ -434,90 +542,301 @@
 		case AE_IFREG:
 			break;
 		case AE_IFDIR:
+		{
 			/*
 			 * Ensure a trailing '/'.  Modify the original
 			 * entry so the client sees the change.
 			 */
-			p = archive_entry_pathname(entry_original);
-			if (p[strlen(p) - 1] != '/') {
-				t = (char *)malloc(strlen(p) + 2);
-				if (t == NULL) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+			const wchar_t *wp;
+
+			wp = archive_entry_pathname_w(entry_original);
+			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 pax data");
+					    "Can't allocate pax data");
+					archive_wstring_free(&ws);
 					return(ARCHIVE_FATAL);
 				}
-				strcpy(t, p);
-				strcat(t, "/");
-				archive_entry_copy_pathname(entry_original, t);
-				free(t);
+				/* 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_original, ws.s);
+				archive_wstring_free(&ws);
+				p = NULL;
+			} else
+#endif
+				p = archive_entry_pathname(entry_original);
+			/*
+			 * On Windows, this is a backup operation just in
+			 * case getting WCS failed. On POSIX, this is a
+			 * normal operation.
+			 */
+			if (p != NULL && 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 pax 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_original, as.s);
+				archive_string_free(&as);
 			}
 			break;
+		}
 		case AE_IFSOCK:
 			archive_set_error(&a->archive,
 			    ARCHIVE_ERRNO_FILE_FORMAT,
 			    "tar format cannot archive socket");
-			return (ARCHIVE_WARN);
+			return (ARCHIVE_FAILED);
 		default:
 			archive_set_error(&a->archive,
 			    ARCHIVE_ERRNO_FILE_FORMAT,
 			    "tar format cannot archive this (type=0%lo)",
-			    (unsigned long)archive_entry_filetype(entry_original));
-			return (ARCHIVE_WARN);
+			    (unsigned long)
+			    archive_entry_filetype(entry_original));
+			return (ARCHIVE_FAILED);
 		}
 	}
 
+	/*
+	 * If Mac OS metadata blob is here, recurse to write that
+	 * as a separate entry.  This is really a pretty poor design:
+	 * In particular, it doubles the overhead for long filenames.
+	 * TODO: Help Apple folks design something better and figure
+	 * out how to transition from this legacy format.
+	 *
+	 * Note that this code is present on every platform; clients
+	 * on non-Mac are unlikely to ever provide this data, but
+	 * applications that copy entries from one archive to another
+	 * should not lose data just because the local filesystem
+	 * can't store it.
+	 */
+	mac_metadata =
+	    archive_entry_mac_metadata(entry_original, &mac_metadata_size);
+	if (mac_metadata != NULL) {
+		const char *oname;
+		char *name, *bname;
+		size_t name_length;
+		struct archive_entry *extra = archive_entry_new2(&a->archive);
+
+		oname = archive_entry_pathname(entry_original);
+		name_length = strlen(oname);
+		name = malloc(name_length + 3);
+		if (name == NULL) {
+			/* XXX error message */
+			return (ARCHIVE_FAILED);
+		}
+		strcpy(name, oname);
+		/* Find last '/'; strip trailing '/' characters */
+		bname = strrchr(name, '/');
+		while (bname != NULL && bname[1] == '\0') {
+			*bname = '\0';
+			bname = strrchr(name, '/');
+		}
+		if (bname == NULL) {
+			memmove(name + 2, name, name_length + 1);
+			memmove(name, "._", 2);
+		} else {
+			bname += 1;
+			memmove(bname + 2, bname, strlen(bname) + 1);
+			memmove(bname, "._", 2);
+		}
+		archive_entry_copy_pathname(extra, name);
+		free(name);
+
+		archive_entry_set_size(extra, mac_metadata_size);
+		archive_entry_set_filetype(extra, AE_IFREG);
+		archive_entry_set_perm(extra,
+		    archive_entry_perm(entry_original));
+		archive_entry_set_mtime(extra,
+		    archive_entry_mtime(entry_original),
+		    archive_entry_mtime_nsec(entry_original));
+		archive_entry_set_gid(extra,
+		    archive_entry_gid(entry_original));
+		archive_entry_set_gname(extra,
+		    archive_entry_gname(entry_original));
+		archive_entry_set_uid(extra,
+		    archive_entry_uid(entry_original));
+		archive_entry_set_uname(extra,
+		    archive_entry_uname(entry_original));
+
+		/* Recurse to write the special copyfile entry. */
+		r = archive_write_pax_header(a, extra);
+		if (r < ARCHIVE_WARN)
+			return (r);
+		if (r < ret)
+			ret = r;
+		r = archive_write_pax_data(a, mac_metadata, mac_metadata_size);
+		if (r < ARCHIVE_WARN)
+			return (r);
+		if (r < ret)
+			ret = r;
+		r = archive_write_pax_finish_entry(a);
+		if (r < ARCHIVE_WARN)
+			return (r);
+		if (r < ret)
+			ret = r;
+	}
+
 	/* Copy entry so we can modify it as needed. */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* Make sure the path separators in pahtname, hardlink and symlink
+	 * are all slash '/', not the Windows path separator '\'. */
+	entry_main = __la_win_entry_in_posix_pathseparator(entry_original);
+	if (entry_main == entry_original)
+		entry_main = archive_entry_clone(entry_original);
+#else
 	entry_main = archive_entry_clone(entry_original);
+#endif
+	if (entry_main == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate pax data");
+		return(ARCHIVE_FATAL);
+	}
 	archive_string_empty(&(pax->pax_header)); /* Blank our work area. */
+	archive_string_empty(&(pax->sparse_map));
+	sparse_total = 0;
+	sparse_list_clear(pax);
+
+	if (hardlink == NULL &&
+	    archive_entry_filetype(entry_main) == AE_IFREG)
+		sparse_count = archive_entry_sparse_reset(entry_main);
+	else
+		sparse_count = 0;
+	if (sparse_count) {
+		int64_t offset, length, last_offset = 0;
+		/* Get the last entry of sparse block. */
+		while (archive_entry_sparse_next(
+		    entry_main, &offset, &length) == ARCHIVE_OK)
+			last_offset = offset + length;
+
+		/* If the last sparse block does not reach the end of file,
+		 * We have to add a empty sparse block as the last entry to
+		 * manage storing file data. */
+		if (last_offset < archive_entry_size(entry_main))
+			archive_entry_sparse_add_entry(entry_main,
+			    archive_entry_size(entry_main), 0);
+		sparse_count = archive_entry_sparse_reset(entry_main);
+	}
 
 	/*
 	 * First, check the name fields and see if any of them
 	 * require binary coding.  If any of them does, then all of
 	 * them do.
 	 */
-	hdrcharset = NULL;
-	path = archive_entry_pathname(entry_main);
-	path_w = archive_entry_pathname_w(entry_main);
-	if (path != NULL && path_w == NULL) {
+	r = get_entry_pathname(a, entry_main, &path, &path_length, sconv);
+	if (r == ARCHIVE_FATAL)
+		return (r);
+	else if (r != ARCHIVE_OK) {
+		r = get_entry_pathname(a, entry_main, &path,
+		    &path_length, NULL);
+		if (r == ARCHIVE_FATAL)
+			return (r);
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Can't translate pathname '%s' to UTF-8", path);
+		    "Can't translate pathname '%s' to %s", path,
+		    archive_string_conversion_charset_name(sconv));
 		ret = ARCHIVE_WARN;
-		hdrcharset = "BINARY";
+		sconv = NULL;/* The header charset switches to binary mode. */
 	}
-	uname = archive_entry_uname(entry_main);
-	uname_w = archive_entry_uname_w(entry_main);
-	if (uname != NULL && uname_w == NULL) {
+	r = get_entry_uname(a, entry_main, &uname, &uname_length, sconv);
+	if (r == ARCHIVE_FATAL)
+		return (r);
+	else if (r != ARCHIVE_OK) {
+		r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL);
+		if (r == ARCHIVE_FATAL)
+			return (r);
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Can't translate uname '%s' to UTF-8", uname);
+		    "Can't translate uname '%s' to %s", uname,
+		    archive_string_conversion_charset_name(sconv));
 		ret = ARCHIVE_WARN;
-		hdrcharset = "BINARY";
+		sconv = NULL;/* The header charset switches to binary mode. */
 	}
-	gname = archive_entry_gname(entry_main);
-	gname_w = archive_entry_gname_w(entry_main);
-	if (gname != NULL && gname_w == NULL) {
+	r = get_entry_gname(a, entry_main, &gname, &gname_length, sconv);
+	if (r == ARCHIVE_FATAL)
+		return (r);
+	else if (r != ARCHIVE_OK) {
+		r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL);
+		if (r == ARCHIVE_FATAL)
+			return (r);
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Can't translate gname '%s' to UTF-8", gname);
+		    "Can't translate gname '%s' to %s", gname,
+		    archive_string_conversion_charset_name(sconv));
 		ret = ARCHIVE_WARN;
-		hdrcharset = "BINARY";
+		sconv = NULL;/* The header charset switches to binary mode. */
 	}
 	linkpath = hardlink;
-	if (linkpath != NULL) {
-		linkpath_w = archive_entry_hardlink_w(entry_main);
-	} else {
-		linkpath = archive_entry_symlink(entry_main);
-		if (linkpath != NULL)
-			linkpath_w = archive_entry_symlink_w(entry_main);
+	linkpath_length = hardlink_length;
+	if (linkpath == NULL) {
+		r = get_entry_symlink(a, entry_main, &linkpath,
+		    &linkpath_length, sconv);
+		if (r == ARCHIVE_FATAL)
+			return (r);
+		else if (r != ARCHIVE_OK) {
+			r = get_entry_symlink(a, entry_main, &linkpath,
+			    &linkpath_length, NULL);
+			if (r == ARCHIVE_FATAL)
+				return (r);
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Can't translate linkname '%s' to %s", linkpath,
+			    archive_string_conversion_charset_name(sconv));
+			ret = ARCHIVE_WARN;
+			sconv = NULL;
+		}
 	}
-	if (linkpath != NULL && linkpath_w == NULL) {
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Can't translate linkpath '%s' to UTF-8", linkpath);
-		ret = ARCHIVE_WARN;
-		hdrcharset = "BINARY";
+
+	/* If any string conversions failed, get all attributes
+	 * in binary-mode. */
+	if (sconv == NULL && !pax->opt_binary) {
+		if (hardlink != NULL) {
+			r = get_entry_hardlink(a, entry_main, &hardlink,
+			    &hardlink_length, NULL);
+			if (r == ARCHIVE_FATAL)
+				return (r);
+			linkpath = hardlink;
+			linkpath_length = hardlink_length;
+		}
+		r = get_entry_pathname(a, entry_main, &path,
+		    &path_length, NULL);
+		if (r == ARCHIVE_FATAL)
+			return (r);
+		r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL);
+		if (r == ARCHIVE_FATAL)
+			return (r);
+		r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL);
+		if (r == ARCHIVE_FATAL)
+			return (r);
 	}
 
 	/* Store the header encoding first, to be nice to readers. */
-	if (hdrcharset != NULL)
-		add_pax_attr(&(pax->pax_header), "hdrcharset", hdrcharset);
+	if (sconv == NULL)
+		add_pax_attr(&(pax->pax_header), "hdrcharset", "BINARY");
 
 
 	/*
@@ -525,38 +844,25 @@
 	 * 'path' to pax extended attrs.  (Note that an unconvertible
 	 * name must have non-ASCII characters.)
 	 */
-	if (path == NULL) {
-		/* We don't have a narrow version, so we have to store
-		 * the wide version. */
-		add_pax_attr_w(&(pax->pax_header), "path", path_w);
-		archive_entry_set_pathname(entry_main, "@WidePath");
-		need_extension = 1;
-	} else if (has_non_ASCII(path_w)) {
+	if (has_non_ASCII(path)) {
 		/* We have non-ASCII characters. */
-		if (path_w == NULL || hdrcharset != NULL) {
-			/* Can't do UTF-8, so store it raw. */
-			add_pax_attr(&(pax->pax_header), "path", path);
-		} else {
-			/* Store UTF-8 */
-			add_pax_attr_w(&(pax->pax_header),
-			    "path", path_w);
-		}
+		add_pax_attr(&(pax->pax_header), "path", path);
 		archive_entry_set_pathname(entry_main,
 		    build_ustar_entry_name(ustar_entry_name,
-			path, strlen(path), NULL));
+			path, path_length, NULL));
 		need_extension = 1;
 	} else {
 		/* We have an all-ASCII path; we'd like to just store
 		 * it in the ustar header if it will fit.  Yes, this
 		 * duplicates some of the logic in
-		 * write_set_format_ustar.c
+		 * archive_write_set_format_ustar.c
 		 */
-		if (strlen(path) <= 100) {
+		if (path_length <= 100) {
 			/* Fits in the old 100-char tar name field. */
 		} else {
 			/* Find largest suffix that will fit. */
 			/* Note: strlen() > 100, so strlen() - 100 - 1 >= 0 */
-			suffix = strchr(path + strlen(path) - 100 - 1, '/');
+			suffix = strchr(path + path_length - 100 - 1, '/');
 			/* Don't attempt an empty prefix. */
 			if (suffix == path)
 				suffix = strchr(suffix + 1, '/');
@@ -571,18 +877,10 @@
 			    || suffix[1] == '\0'    /* empty suffix */
 			    || suffix - path > 155)  /* Prefix > 155 chars */
 			{
-				if (path_w == NULL || hdrcharset != NULL) {
-					/* Can't do UTF-8, so store it raw. */
-					add_pax_attr(&(pax->pax_header),
-					    "path", path);
-				} else {
-					/* Store UTF-8 */
-					add_pax_attr_w(&(pax->pax_header),
-					    "path", path_w);
-				}
+				add_pax_attr(&(pax->pax_header), "path", path);
 				archive_entry_set_pathname(entry_main,
 				    build_ustar_entry_name(ustar_entry_name,
-					path, strlen(path), NULL));
+					path, path_length, NULL));
 				need_extension = 1;
 			}
 		}
@@ -591,21 +889,9 @@
 	if (linkpath != NULL) {
 		/* If link name is too long or has non-ASCII characters, add
 		 * 'linkpath' to pax extended attrs. */
-		if (strlen(linkpath) > 100 || linkpath_w == NULL
-		    || linkpath_w == NULL || has_non_ASCII(linkpath_w)) {
-			if (linkpath_w == NULL || hdrcharset != NULL)
-				/* If the linkpath is