git: 3a77c21a25e1 - stable/13 - libarchive: merge from vendor branch

From: Martin Matuska <mm_at_FreeBSD.org>
Date: Sat, 04 May 2024 12:37:43 UTC
The branch stable/13 has been updated by mm:

URL: https://cgit.FreeBSD.org/src/commit/?id=3a77c21a25e12fd60cc6093c74d4ad1a7f247319

commit 3a77c21a25e12fd60cc6093c74d4ad1a7f247319
Author:     Martin Matuska <mm@FreeBSD.org>
AuthorDate: 2024-04-29 08:15:04 +0000
Commit:     Martin Matuska <mm@FreeBSD.org>
CommitDate: 2024-05-04 11:54:42 +0000

    libarchive: merge from vendor branch
    
    Libarchive 3.7.4 + three fixes from master
    
    Security fixes:
     #2135 rar: Fix OOB in rar e8 filter (CVE-2024-26256)
     #2145 zip: Fix out of boundary access
     #2148 rar: Fix OOB in rar delta filter
     #2149 rar: Fix OOB in rar audio filter
    
    Important bugfixes:
     #2131 7zip: Limit amount of properties
     #2110 bsdtar: Fix error handling around strtol() usages
     #2116 passphrase: Never allow empty passwords
     #2124 rar: Fix "File CRC Error" when extracting specific rar4 archives
     #2123 xar: Avoid infinite link loop
     #2150 xar: Fix another infinite loop and expat error handling
     #2108 zip: Update AppleDouble support for directories
     #2071 zstd: Implement core detectiongit
    
    PR:     278662 (exp-run)
    
    (cherry picked from commit 13d826ff947d9026f98e317e7385b22abfc0eace)
---
 contrib/libarchive/NEWS                            |  2 +
 contrib/libarchive/README.md                       |  2 +-
 contrib/libarchive/cat/cmdline.c                   | 16 +++--
 contrib/libarchive/cpio/cmdline.c                  | 16 +++--
 contrib/libarchive/libarchive/archive.h            |  6 +-
 contrib/libarchive/libarchive/archive_entry.h      |  2 +-
 contrib/libarchive/libarchive/archive_entry_acl.3  |  2 +-
 contrib/libarchive/libarchive/archive_read_disk.3  |  4 +-
 .../libarchive/archive_read_support_format_7zip.c  |  2 +
 .../libarchive/archive_read_support_format_all.c   |  2 +-
 .../libarchive/archive_read_support_format_ar.c    |  4 +-
 .../libarchive/archive_read_support_format_lha.c   |  4 +-
 .../libarchive/archive_read_support_format_mtree.c |  8 +--
 .../libarchive/archive_read_support_format_rar.c   | 30 +++++++-
 .../libarchive/archive_read_support_format_warc.c  | 10 ++-
 .../libarchive/archive_read_support_format_xar.c   |  9 +++
 .../libarchive/archive_read_support_format_zip.c   | 15 +++-
 contrib/libarchive/libarchive/archive_util.c       |  3 +-
 .../libarchive/archive_write_add_filter_zstd.c     | 23 +++++-
 .../libarchive/archive_write_disk_posix.c          |  3 +-
 .../libarchive/libarchive/archive_write_private.h  |  2 +-
 .../libarchive/archive_write_set_format_gnutar.c   |  2 +-
 .../libarchive/archive_write_set_passphrase.c      | 35 ++++-----
 .../libarchive/libarchive/libarchive_internals.3   |  2 +-
 .../test/test_read_format_xar_doublelink.c         | 55 ++++++++++++++
 .../test/test_read_format_xar_doublelink.xar.uu    | 12 ++++
 .../libarchive/test/test_write_disk_appledouble.c  | 84 ++++++++++++++++++++++
 .../test/test_write_disk_appledouble_zip.zip.uu    | 27 +++++++
 contrib/libarchive/libarchive_fe/passphrase.c      |  4 +-
 contrib/libarchive/tar/bsdtar.1                    | 15 ++--
 contrib/libarchive/tar/bsdtar.c                    | 63 ++++++++--------
 contrib/libarchive/tar/cmdline.c                   | 16 +++--
 contrib/libarchive/unzip/cmdline.c                 | 18 +++--
 contrib/libarchive/unzip/test/test_I.c             | 13 ++++
 lib/libarchive/tests/Makefile                      |  3 +
 35 files changed, 404 insertions(+), 110 deletions(-)

diff --git a/contrib/libarchive/NEWS b/contrib/libarchive/NEWS
index f4395fd1c979..ebdbb2a978ec 100644
--- a/contrib/libarchive/NEWS
+++ b/contrib/libarchive/NEWS
@@ -1,3 +1,5 @@
+Apr 26, 2024: libarchive 3.7.4 released
+
 Apr 08, 2024: libarchive 3.7.3 released
 
 Sep 12, 2023: libarchive 3.7.2 released
diff --git a/contrib/libarchive/README.md b/contrib/libarchive/README.md
index 727ed49856b6..933de6986425 100644
--- a/contrib/libarchive/README.md
+++ b/contrib/libarchive/README.md
@@ -201,7 +201,7 @@ questions we are asked about libarchive:
   In case other thread calls the same function in parallel, it might
   get interrupted by it and cause the executable to use umask=0 for the
   remaining execution.
-  This will then lead to implicitely created directories to have 777
+  This will then lead to implicitly created directories to have 777
   permissions without sticky bit.
 
 * In particular, libarchive's modules to read or write a directory
diff --git a/contrib/libarchive/cat/cmdline.c b/contrib/libarchive/cat/cmdline.c
index ea1e0eed6d0a..851b63de06e5 100644
--- a/contrib/libarchive/cat/cmdline.c
+++ b/contrib/libarchive/cat/cmdline.c
@@ -114,12 +114,18 @@ bsdcat_getopt(struct bsdcat *bsdcat)
 	enum { state_start = 0, state_old_tar, state_next_word,
 	       state_short, state_long };
 
-	const struct bsdcat_option *popt, *match = NULL, *match2 = NULL;
-	const char *p, *long_prefix = "--";
+	const struct bsdcat_option *popt, *match, *match2;
+	const char *p, *long_prefix;
 	size_t optlength;
-	int opt = '?';
-	int required = 0;
+	int opt;
+	int required;
 
+again:
+	match = NULL;
+	match2 = NULL;
+	long_prefix = "--";
+	opt = '?';
+	required = 0;
 	bsdcat->argument = NULL;
 
 	/* First time through, initialize everything. */
@@ -172,7 +178,7 @@ bsdcat_getopt(struct bsdcat *bsdcat)
 		if (opt == '\0') {
 			/* End of this group; recurse to get next option. */
 			bsdcat->getopt_state = state_next_word;
-			return bsdcat_getopt(bsdcat);
+			goto again;
 		}
 
 		/* Does this option take an argument? */
diff --git a/contrib/libarchive/cpio/cmdline.c b/contrib/libarchive/cpio/cmdline.c
index 312d762c8f46..ab25492ede48 100644
--- a/contrib/libarchive/cpio/cmdline.c
+++ b/contrib/libarchive/cpio/cmdline.c
@@ -114,12 +114,18 @@ cpio_getopt(struct cpio *cpio)
 	static int state = state_start;
 	static char *opt_word;
 
-	const struct option *popt, *match = NULL, *match2 = NULL;
-	const char *p, *long_prefix = "--";
+	const struct option *popt, *match, *match2;
+	const char *p, *long_prefix;
 	size_t optlength;
-	int opt = '?';
-	int required = 0;
+	int opt;
+	int required;
 
+again:
+	match = NULL;
+	match2 = NULL;
+	long_prefix = "--";
+	opt = '?';
+	required = 0;
 	cpio->argument = NULL;
 
 	/* First time through, initialize everything. */
@@ -169,7 +175,7 @@ cpio_getopt(struct cpio *cpio)
 		if (opt == '\0') {
 			/* End of this group; recurse to get next option. */
 			state = state_next_word;
-			return cpio_getopt(cpio);
+			goto again;
 		}
 
 		/* Does this option take an argument? */
diff --git a/contrib/libarchive/libarchive/archive.h b/contrib/libarchive/libarchive/archive.h
index 2e3a9f31cd33..fd4dd20fad13 100644
--- a/contrib/libarchive/libarchive/archive.h
+++ b/contrib/libarchive/libarchive/archive.h
@@ -34,7 +34,7 @@
  * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
  */
 /* Note: Compiler will complain if this does not match archive_entry.h! */
-#define	ARCHIVE_VERSION_NUMBER 3007003
+#define	ARCHIVE_VERSION_NUMBER 3007004
 
 #include <sys/stat.h>
 #include <stddef.h>  /* for wchar_t */
@@ -155,7 +155,7 @@ __LA_DECL int		archive_version_number(void);
 /*
  * Textual name/version of the library, useful for version displays.
  */
-#define	ARCHIVE_VERSION_ONLY_STRING "3.7.3"
+#define	ARCHIVE_VERSION_ONLY_STRING "3.7.4"
 #define	ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
 __LA_DECL const char *	archive_version_string(void);
 
@@ -895,7 +895,7 @@ __LA_DECL int archive_write_set_options(struct archive *_a,
 			    const char *opts);
 
 /*
- * Set a encryption passphrase.
+ * Set an encryption passphrase.
  */
 __LA_DECL int archive_write_set_passphrase(struct archive *_a, const char *p);
 __LA_DECL int archive_write_set_passphrase_callback(struct archive *,
diff --git a/contrib/libarchive/libarchive/archive_entry.h b/contrib/libarchive/libarchive/archive_entry.h
index df9cb765f7e8..1c59ded7c911 100644
--- a/contrib/libarchive/libarchive/archive_entry.h
+++ b/contrib/libarchive/libarchive/archive_entry.h
@@ -28,7 +28,7 @@
 #define	ARCHIVE_ENTRY_H_INCLUDED
 
 /* Note: Compiler will complain if this does not match archive.h! */
-#define	ARCHIVE_VERSION_NUMBER 3007003
+#define	ARCHIVE_VERSION_NUMBER 3007004
 
 /*
  * Note: archive_entry.h is for use outside of libarchive; the
diff --git a/contrib/libarchive/libarchive/archive_entry_acl.3 b/contrib/libarchive/libarchive/archive_entry_acl.3
index 50dd642c20c6..4d0d8b50ed07 100644
--- a/contrib/libarchive/libarchive/archive_entry_acl.3
+++ b/contrib/libarchive/libarchive/archive_entry_acl.3
@@ -383,7 +383,7 @@ Prefix each default ACL entry with the word
 The mask and other ACLs don not contain a double colon.
 .El
 .Pp
-The following flags are effecive only on NFSv4 ACL:
+The following flags are effective only on NFSv4 ACL:
 .Bl -tag -offset indent -compact -width ARCHIV
 .It Dv ARCHIVE_ENTRY_ACL_STYLE_COMPACT
 Do not output minus characters for unset permissions and flags in NFSv4 ACL
diff --git a/contrib/libarchive/libarchive/archive_read_disk.3 b/contrib/libarchive/libarchive/archive_read_disk.3
index 7cde3c232713..990c1514c4d5 100644
--- a/contrib/libarchive/libarchive/archive_read_disk.3
+++ b/contrib/libarchive/libarchive/archive_read_disk.3
@@ -288,11 +288,11 @@ calls. If matched based on calls to
 .Tn archive_match_time_excluded ,
 or
 .Tn archive_match_owner_excluded ,
-then the callback function specified by the _excluded_func parameter will execute. This function will recieve data provided to the fourth parameter, void *_client_data.
+then the callback function specified by the _excluded_func parameter will execute. This function will receive data provided to the fourth parameter, void *_client_data.
 .It Fn archive_read_disk_set_metadata_filter_callback
 Allows the caller to set a callback function during calls to
 .Xr archive_read_header 3
-to filter out metadata for each entry. The callback function recieves the
+to filter out metadata for each entry. The callback function receives the
 .Tn struct archive
 object, void* custom filter data, and the 
 .Tn struct archive_entry .
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_7zip.c b/contrib/libarchive/libarchive/archive_read_support_format_7zip.c
index 92495e628f9a..7e465935c902 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_7zip.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_7zip.c
@@ -2037,6 +2037,8 @@ read_Folder(struct archive_read *a, struct _7z_folder *f)
 			if (parse_7zip_uint64(
 			    a, &(f->coders[i].propertiesSize)) < 0)
 				return (-1);
+			if (UMAX_ENTRY < f->coders[i].propertiesSize)
+				return (-1);
 			if ((p = header_bytes(
 			    a, (size_t)f->coders[i].propertiesSize)) == NULL)
 				return (-1);
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_all.c b/contrib/libarchive/libarchive/archive_read_support_format_all.c
index 5a4e1ab675a5..3b53c9ad5f57 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_all.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_all.c
@@ -67,7 +67,7 @@ archive_read_support_format_all(struct archive *a)
 	 * 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. */
+	/* These have potentially large look-ahead. */
 	archive_read_support_format_7zip(a);
 	archive_read_support_format_cab(a);
 	archive_read_support_format_rar(a);
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_ar.c b/contrib/libarchive/libarchive/archive_read_support_format_ar.c
index ca8effb0b0ee..6f1be8591fef 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_ar.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_ar.c
@@ -270,7 +270,7 @@ _ar_read_header(struct archive_read *a, struct archive_entry *entry,
 		}
 		if (ar->strtab != NULL) {
 			archive_set_error(&a->archive, EINVAL,
-			    "More than one string tables exist");
+			    "More than one string table exists");
 			return (ARCHIVE_FATAL);
 		}
 
@@ -515,7 +515,7 @@ archive_read_format_ar_read_data(struct archive_read *a,
 		if (ar->entry_padding) {
 			if (skipped >= 0) {
 				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
-					"Truncated ar archive- failed consuming padding");
+					"Truncated ar archive - failed consuming padding");
 			}
 			return (ARCHIVE_FATAL);
 		}
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_lha.c b/contrib/libarchive/libarchive/archive_read_support_format_lha.c
index 1c64b2900b8e..4d6290ac33bb 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_lha.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_lha.c
@@ -1693,7 +1693,7 @@ archive_read_format_lha_cleanup(struct archive_read *a)
  * example.
  *   1. a symbolic-name is 'aaa/bb/cc'
  *   2. a filename is 'xxx/bbb'
- *  then a archived pathname is 'xxx/bbb|aaa/bb/cc'
+ *  then an archived pathname is 'xxx/bbb|aaa/bb/cc'
  */
 static int
 lha_parse_linkname(struct archive_wstring *linkname,
@@ -2385,7 +2385,7 @@ lzh_decode_blocks(struct lzh_stream *strm, int last)
 					return (100);
 				}
 
-				/* lzh_br_read_ahead() always try to fill the
+				/* lzh_br_read_ahead() always tries to fill the
 				 * cache buffer up. In specific situation we
 				 * are close to the end of the data, the cache
 				 * buffer will not be full and thus we have to
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_mtree.c b/contrib/libarchive/libarchive/archive_read_support_format_mtree.c
index 630cff6e3999..6971228eefad 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_mtree.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_mtree.c
@@ -416,8 +416,8 @@ next_line(struct archive_read *a,
 }
 
 /*
- * Compare characters with a mtree keyword.
- * Returns the length of a mtree keyword if matched.
+ * Compare characters with an mtree keyword.
+ * Returns the length of an mtree keyword if matched.
  * Returns 0 if not matched.
  */
 static int
@@ -515,7 +515,7 @@ bid_keyword(const char *p,  ssize_t len)
 
 /*
  * Test whether there is a set of mtree keywords.
- * Returns the number of keyword.
+ * Returns the number of keywords.
  * 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".
@@ -760,7 +760,7 @@ detect_form(struct archive_read *a, int *is_form_d)
 					multiline = 1;
 				else {
 					/* We've got plenty of correct lines
-					 * to assume that this file is a mtree
+					 * to assume that this file is an mtree
 					 * format. */
 					if (++entry_cnt >= MAX_BID_ENTRY)
 						break;
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_rar.c b/contrib/libarchive/libarchive/archive_read_support_format_rar.c
index 99a11d170074..4fc6626cacfd 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_rar.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_rar.c
@@ -2176,6 +2176,19 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size,
     {
       start = rar->offset;
       end = start + rar->dictionary_size;
+
+      /* We don't want to overflow the window and overwrite data that we write
+       * at 'start'. Therefore, reduce the end length by the maximum match size,
+       * which is 260 bytes. You can compute this maximum by looking at the
+       * definition of 'expand', in particular when 'symbol >= 271'. */
+      /* NOTE: It's possible for 'dictionary_size' to be less than this 260
+       * value, however that will only be the case when 'unp_size' is small,
+       * which should only happen when the entry size is small and there's no
+       * risk of overflowing the buffer */
+      if (rar->dictionary_size > 260) {
+        end -= 260;
+      }
+
       if (rar->filters.filterstart < end) {
         end = rar->filters.filterstart;
       }
@@ -3599,7 +3612,15 @@ execute_filter_delta(struct rar_filter *filter, struct rar_virtual_machine *vm)
   {
     uint8_t lastbyte = 0;
     for (idx = i; idx < length; idx += numchannels)
+    {
+      /*
+       * The src block should not overlap with the dst block.
+       * If so it would be better to consider this archive is broken.
+       */
+      if (src >= dst)
+        return 0;
       lastbyte = dst[idx] = lastbyte - *src++;
+    }
   }
 
   filter->filteredblockaddress = length;
@@ -3615,7 +3636,7 @@ execute_filter_e8(struct rar_filter *filter, struct rar_virtual_machine *vm, siz
   uint32_t filesize = 0x1000000;
   uint32_t i;
 
-  if (length > PROGRAM_WORK_SIZE || length < 4)
+  if (length > PROGRAM_WORK_SIZE || length <= 4)
     return 0;
 
   for (i = 0; i <= length - 5; i++)
@@ -3701,6 +3722,13 @@ execute_filter_audio(struct rar_filter *filter, struct rar_virtual_machine *vm)
     memset(&state, 0, sizeof(state));
     for (j = i; j < length; j += numchannels)
     {
+      /*
+       * The src block should not overlap with the dst block.
+       * If so it would be better to consider this archive is broken.
+       */
+      if (src >= dst)
+        return 0;
+
       int8_t delta = (int8_t)*src++;
       uint8_t predbyte, byte;
       int prederror;
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_warc.c b/contrib/libarchive/libarchive/archive_read_support_format_warc.c
index c49d44eba5e5..fcec5bc4cbb9 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_warc.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_warc.c
@@ -215,6 +215,7 @@ _warc_rdhdr(struct archive_read *a, struct archive_entry *entry)
 	const char *buf;
 	ssize_t nrd;
 	const char *eoh;
+	char *tmp;
 	/* for the file name, saves some strndup()'ing */
 	warc_string_t fnam;
 	/* warc record type, not that we really use it a lot */
@@ -321,7 +322,14 @@ start_over:
 		 * malloc()+free() roundtrip */
 		if (fnam.len + 1U > w->pool.len) {
 			w->pool.len = ((fnam.len + 64U) / 64U) * 64U;
-			w->pool.str = realloc(w->pool.str, w->pool.len);
+			tmp = realloc(w->pool.str, w->pool.len);
+			if (tmp == NULL) {
+				archive_set_error(
+					&a->archive, ENOMEM,
+					"Out of memory");
+				return (ARCHIVE_FATAL);
+			}
+			w->pool.str = tmp;
 		}
 		memcpy(w->pool.str, fnam.str, fnam.len);
 		w->pool.str[fnam.len] = '\0';
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_xar.c b/contrib/libarchive/libarchive/archive_read_support_format_xar.c
index fd63594373cb..cefb36410e77 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_xar.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_xar.c
@@ -2055,6 +2055,12 @@ xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list)
 			    attr = attr->next) {
 				if (strcmp(attr->name, "link") != 0)
 					continue;
+				if (xar->file->hdnext != NULL || xar->file->link != 0 ||
+				    xar->file == xar->hdlink_orgs) {
+					archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+					    "File with multiple link attributes");
+					return (ARCHIVE_FATAL);
+				}
 				if (strcmp(attr->value, "original") == 0) {
 					xar->file->hdnext = xar->hdlink_orgs;
 					xar->hdlink_orgs = xar->file;
@@ -3251,6 +3257,9 @@ expat_start_cb(void *userData, const XML_Char *name, const XML_Char **atts)
 	struct xmlattr_list list;
 	int r;
 
+	if (ud->state != ARCHIVE_OK)
+		return;
+
 	r = expat_xmlattr_setup(a, &list, atts);
 	if (r == ARCHIVE_OK)
 		r = xml_start(a, (const char *)name, &list);
diff --git a/contrib/libarchive/libarchive/archive_read_support_format_zip.c b/contrib/libarchive/libarchive/archive_read_support_format_zip.c
index 212bfff9fa7b..c9759eaf9a89 100644
--- a/contrib/libarchive/libarchive/archive_read_support_format_zip.c
+++ b/contrib/libarchive/libarchive/archive_read_support_format_zip.c
@@ -1393,7 +1393,7 @@ check_authentication_code(struct archive_read *a, const void *_p)
  *  [CRC32] [compressed low] [compressed high] [uncompressed low] [uncompressed high] [other PK marker]
  * ```
  * Since the 32-bit and 64-bit compressed sizes both match, the
- * actualy size must fit in 32 bits, which implies the high-order
+ * actual size must fit in 32 bits, which implies the high-order
  * word of the compressed size is zero.  So we know the uncompressed
  * low word is zero, which again implies that if we accept the shorter
  * format, there will not be a valid PK marker following it.
@@ -4083,6 +4083,17 @@ slurp_central_directory(struct archive_read *a, struct archive_entry* entry,
 			} else {
 				/* Generate resource fork name to find its
 				 * resource file at zip->tree_rsrc. */
+
+				/* If this is an entry ending with slash,
+				 * make the resource for name slash-less
+				 * as the actual resource fork doesn't end with '/'.
+				 */
+				size_t tmp_length = filename_length;
+				if (tmp_length > 0 && name[tmp_length - 1] == '/') {
+					tmp_length--;
+					r = rsrc_basename(name, tmp_length);
+				}
+
 				archive_strcpy(&(zip_entry->rsrcname),
 				    "__MACOSX/");
 				archive_strncat(&(zip_entry->rsrcname),
@@ -4090,7 +4101,7 @@ slurp_central_directory(struct archive_read *a, struct archive_entry* entry,
 				archive_strcat(&(zip_entry->rsrcname), "._");
 				archive_strncat(&(zip_entry->rsrcname),
 				    name + (r - name),
-				    filename_length - (r - name));
+				    tmp_length - (r - name));
 				/* Register an entry to RB tree to sort it by
 				 * file offset. */
 				__archive_rb_tree_insert_node(&zip->tree,
diff --git a/contrib/libarchive/libarchive/archive_util.c b/contrib/libarchive/libarchive/archive_util.c
index 32d4bd40988c..7b918fef04b8 100644
--- a/contrib/libarchive/libarchive/archive_util.c
+++ b/contrib/libarchive/libarchive/archive_util.c
@@ -255,10 +255,9 @@ __archive_mktempx(const char *tmpdir, wchar_t *template)
 #endif
 	fd = -1;
 	ws = NULL;
+	archive_string_init(&temp_name);
 
 	if (template == NULL) {
-		archive_string_init(&temp_name);
-
 		/* Get a temporary directory. */
 		if (tmpdir == NULL) {
 			size_t l;
diff --git a/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c b/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c
index 94249accd08b..7ea3d18c9b76 100644
--- a/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c
+++ b/contrib/libarchive/libarchive/archive_write_add_filter_zstd.c
@@ -29,6 +29,9 @@
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
 #ifdef HAVE_STDINT_H
 #include <stdint.h>
 #endif
@@ -38,6 +41,9 @@
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 #ifdef HAVE_ZSTD_H
 #include <zstd.h>
 #endif
@@ -190,6 +196,7 @@ string_to_number(const char *string, intmax_t *numberp)
 	return (ARCHIVE_OK);
 }
 
+#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
 static int
 string_to_size(const char *string, size_t *numberp)
 {
@@ -224,6 +231,7 @@ string_to_size(const char *string, size_t *numberp)
 	*numberp = (size_t)(number << shift);
 	return (ARCHIVE_OK);
 }
+#endif
 
 /*
  * Set write options.
@@ -264,7 +272,20 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
 		if (string_to_number(value, &threads) != ARCHIVE_OK) {
 			return (ARCHIVE_WARN);
 		}
-		if (threads < 0) {
+
+#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
+		if (threads == 0) {
+			threads = sysconf(_SC_NPROCESSORS_ONLN);
+		}
+#elif !defined(__CYGWIN__) && defined(_WIN32_WINNT) && \
+    _WIN32_WINNT >= 0x0601 /* _WIN32_WINNT_WIN7 */
+		if (threads == 0) {
+			DWORD winCores = GetActiveProcessorCount(
+			    ALL_PROCESSOR_GROUPS);
+			threads = (intmax_t)winCores;
+		}
+#endif
+		if (threads < 0 || threads > INT_MAX) {
 			return (ARCHIVE_WARN);
 		}
 		data->threads = (int)threads;
diff --git a/contrib/libarchive/libarchive/archive_write_disk_posix.c b/contrib/libarchive/libarchive/archive_write_disk_posix.c
index f671bc6db747..d69c77ea0517 100644
--- a/contrib/libarchive/libarchive/archive_write_disk_posix.c
+++ b/contrib/libarchive/libarchive/archive_write_disk_posix.c
@@ -4427,7 +4427,8 @@ fixup_appledouble(struct archive_write_disk *a, const char *pathname)
 #else
 		la_stat(datafork.s, &st) == -1 ||
 #endif
-	    (st.st_mode & AE_IFMT) != AE_IFREG)
+	    (((st.st_mode & AE_IFMT) != AE_IFREG) &&
+		((st.st_mode & AE_IFMT) != AE_IFDIR)))
 		goto skip_appledouble;
 
 	/*
diff --git a/contrib/libarchive/libarchive/archive_write_private.h b/contrib/libarchive/libarchive/archive_write_private.h
index abd5a8ddcd85..f259ccb16546 100644
--- a/contrib/libarchive/libarchive/archive_write_private.h
+++ b/contrib/libarchive/libarchive/archive_write_private.h
@@ -158,7 +158,7 @@ int	__archive_write_program_write(struct archive_write_filter *,
 	    struct archive_write_program_data *, const void *, size_t);
 
 /*
- * Get a encryption passphrase.
+ * Get an encryption passphrase.
  */
 const char * __archive_write_get_passphrase(struct archive_write *a);
 #endif
diff --git a/contrib/libarchive/libarchive/archive_write_set_format_gnutar.c b/contrib/libarchive/libarchive/archive_write_set_format_gnutar.c
index 92b06c5f5fb4..a88350b87411 100644
--- a/contrib/libarchive/libarchive/archive_write_set_format_gnutar.c
+++ b/contrib/libarchive/libarchive/archive_write_set_format_gnutar.c
@@ -387,7 +387,7 @@ archive_write_gnutar_header(struct archive_write *a,
 	if (r != 0) {
 		if (errno == ENOMEM) {
 			archive_set_error(&a->archive, ENOMEM,
-			    "Can't allocate memory for Pathame");
+			    "Can't allocate memory for pathname");
 			ret = ARCHIVE_FATAL;
 			goto exit_write_header;
 		}
diff --git a/contrib/libarchive/libarchive/archive_write_set_passphrase.c b/contrib/libarchive/libarchive/archive_write_set_passphrase.c
index 977fc4a9ee6b..f871c8e2f810 100644
--- a/contrib/libarchive/libarchive/archive_write_set_passphrase.c
+++ b/contrib/libarchive/libarchive/archive_write_set_passphrase.c
@@ -30,14 +30,9 @@
 #endif
 #include "archive_write_private.h"
 
-int
-archive_write_set_passphrase(struct archive *_a, const char *p)
+static int
+set_passphrase(struct archive_write *a, const char *p)
 {
-	struct archive_write *a = (struct archive_write *)_a;
-
-	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW,
-		"archive_write_set_passphrase");
-
 	if (p == NULL || p[0] == '\0') {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Empty passphrase is unacceptable");
@@ -54,6 +49,18 @@ archive_write_set_passphrase(struct archive *_a, const char *p)
 }
 
 
+int
+archive_write_set_passphrase(struct archive *_a, const char *p)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW,
+		"archive_write_set_passphrase");
+
+	return (set_passphrase(a, p));
+}
+
+
 int
 archive_write_set_passphrase_callback(struct archive *_a, void *client_data,
     archive_passphrase_callback *cb)
@@ -80,15 +87,9 @@ __archive_write_get_passphrase(struct archive_write *a)
 		const char *p;
 		p = a->passphrase_callback(&a->archive,
 		    a->passphrase_client_data);
-		if (p != NULL) {
-			a->passphrase = strdup(p);
-			if (a->passphrase == NULL) {
-				archive_set_error(&a->archive, ENOMEM,
-				    "Can't allocate data for passphrase");
-				return (NULL);
-			}
-			return (a->passphrase);
-		}
+		set_passphrase(a, p);
+		a->passphrase_callback = NULL;
+		a->passphrase_client_data = NULL;
 	}
-	return (NULL);
+	return (a->passphrase);
 }
diff --git a/contrib/libarchive/libarchive/libarchive_internals.3 b/contrib/libarchive/libarchive/libarchive_internals.3
index d4696f648292..2978b48c3e97 100644
--- a/contrib/libarchive/libarchive/libarchive_internals.3
+++ b/contrib/libarchive/libarchive/libarchive_internals.3
@@ -124,7 +124,7 @@ to read the entire file into memory at once and return the
 entire file to libarchive as a single block;
 other clients may begin asynchronous I/O operations for the
 next block on each request.
-.Ss Decompresssion Layer
+.Ss Decompression Layer
 The decompression layer not only handles decompression,
 it also buffers data so that the format handlers see a
 much nicer I/O model.
diff --git a/contrib/libarchive/libarchive/test/test_read_format_xar_doublelink.c b/contrib/libarchive/libarchive/test/test_read_format_xar_doublelink.c
new file mode 100644
index 000000000000..78d6626a09bf
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_xar_doublelink.c
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2024 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+
+#define __LIBARCHIVE_BUILD
+
+DEFINE_TEST(test_read_format_xar_doublelink)
+{
+	const char *refname = "test_read_format_xar_doublelink.xar";
+	struct archive *a;
+	struct archive_entry *ae;
+
+	extract_reference_file(refname);
+
+	/* Verify with seeking reader. */
+	assert((a = archive_read_new()) != NULL);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+        if(ARCHIVE_OK != archive_read_support_format_xar(a)) {
+                skipping("XAR format unsupported");
+                assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+                return;
+        }
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname,
+	    10240));
+
+	assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae));
+	assertEqualString(archive_error_string(a),
+		"File with multiple link attributes");
+	assert(archive_errno(a) != 0);
+
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
diff --git a/contrib/libarchive/libarchive/test/test_read_format_xar_doublelink.xar.uu b/contrib/libarchive/libarchive/test/test_read_format_xar_doublelink.xar.uu
new file mode 100644
index 000000000000..7aa638a86e7c
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_read_format_xar_doublelink.xar.uu
@@ -0,0 +1,12 @@
+begin 664 test_read_format_xar_doublelink.xar
+M>&%R(0`<``$````````!0`````````/7`````7B<[9/!<L(@%$7W?@7#/H60
+MU'0R!'?]`KOICDF>D3&``]'1?GT!-1U;;:=[5[G<=WB0=P>^..@![<%Y94V#
+M\R>*$9C6=LKT#7Y;OF8O>"%F_""=F"$^VC9\$&\=R#'LR$:E03#*RHR6&2N6
+MM*KIO,YS3JZ1M&D-[<;O-/+C<8`&^[7,<:P@;E<K#Z.@G)Q5<KWZB,TY22*V
+M()<>:;52`R#5A6N?VQ@9CHIN.#_IY(['+:!!F4V#K5.],G+`8BU=%SU.8OF?
+MH#*V`U%5K"@9)Z=5*G2P5RT8*YY+3J9%*ND(T?D\%/3$[U0G<DK#+T9ULCPX
+MH75PHDA6/U']A>J=W6T3=E+);&^E4=0%?0^#N\00;G(;8U7`]!<F?\'D%"J)
+MX[Y.@WU/@]U)(_\SACO$8_X_YA_&$]\F)^FE?@)<4AJ<B%QTTZN3JTL:$,<5
+<`XH;(KD-Q0=XG.V3P7+"(!1%]WX%PSZ%D-1T,@``
+`
+end
diff --git a/contrib/libarchive/libarchive/test/test_write_disk_appledouble.c b/contrib/libarchive/libarchive/test/test_write_disk_appledouble.c
index 3265a94d2fed..8de6c8b50413 100644
--- a/contrib/libarchive/libarchive/test/test_write_disk_appledouble.c
+++ b/contrib/libarchive/libarchive/test/test_write_disk_appledouble.c
@@ -236,3 +236,87 @@ DEFINE_TEST(test_write_disk_appledouble)
 	assertEqualFile("hfscmp/file3", "nocmp/file3");
 #endif
 }
+
+/* Test writing apple doubles to disk from zip format */
+DEFINE_TEST(test_write_disk_appledouble_zip)
+{
+#if !defined(__APPLE__) || !defined(UF_COMPRESSED) || !defined(HAVE_SYS_XATTR_H)\
+	|| !defined(HAVE_ZLIB_H)
+	skipping("MacOS-specific AppleDouble test");
+#else
+	const char *refname = "test_write_disk_appledouble_zip.zip";
+	struct archive *ad, *a;
+	struct archive_entry *ae;
+	struct stat st;
+
+	extract_reference_file(refname);
+
+	/*
+	 * Extract an archive to disk.
+	 */
+	assert((ad = archive_write_disk_new()) != NULL);
+	assertEqualIntA(ad, ARCHIVE_OK,
+	    archive_write_disk_set_standard_lookup(ad));
+	assertEqualIntA(ad, ARCHIVE_OK,
+	    archive_write_disk_set_options(ad,
+		ARCHIVE_EXTRACT_TIME |
+		ARCHIVE_EXTRACT_SECURE_SYMLINKS |
+		ARCHIVE_EXTRACT_SECURE_NODOTDOT));
+
+	assert((a = archive_read_new()) != NULL);
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a,
+	    refname, 512 * 20));
+
+	/* Skip The top level directory */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("apple_double_dir/", archive_entry_pathname(ae));
+
+	/* Extract apple_double_test */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("apple_double_dir/apple_double_dir_test/", archive_entry_pathname(ae));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
+
+	/* Extract ._apple_double_dir_test which will be merged into apple_double_dir_test as metadata. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("apple_double_dir/._apple_double_dir_test", archive_entry_pathname(ae));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
+
+	/* Extract test_file */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("apple_double_dir/test_file", archive_entry_pathname(ae));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
+
+	/* Extract ._test_file which will be merged into test_file as metadata. */
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+	assertEqualString("apple_double_dir/._test_file", archive_entry_pathname(ae));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_extract2(a, ae, ad));
+
+	assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+	assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+	assertEqualIntA(ad, ARCHIVE_OK, archive_write_free(ad));
+
+	/* Test test_file */
+	assertEqualInt(0, stat("apple_double_dir/test_file", &st));
+	assertFileSize("apple_double_dir/test_file", 5);
+	failure("'%s' should have Resource Fork", "test_file");
+	assertEqualInt(1, has_xattr("apple_double_dir/test_file", "com.apple.ResourceFork"));
+
+	/* Test apple_double_dir_test */
+	failure("'%s' should have quarantine xattr", "apple_double_dir_test");
+	assertEqualInt(1, has_xattr("apple_double_dir/apple_double_dir_test", "com.apple.quarantine"));
+
+	/* Test ._test_file. */
+	failure("'apple_double_dir/._test_file' should be merged and removed");
+	assertFileNotExists("apple_double_dir/._test_file");
+
+	/* Test ._apple_double_dir_test */
+	failure("'apple_double_dir/._._apple_double_dir_test' should be merged and removed");
+	assertFileNotExists("apple_double_dir/._apple_double_dir_test");
+
+	assertChdir("..");
+
+#endif
+}
diff --git a/contrib/libarchive/libarchive/test/test_write_disk_appledouble_zip.zip.uu b/contrib/libarchive/libarchive/test/test_write_disk_appledouble_zip.zip.uu
new file mode 100644
index 000000000000..5ab67533d559
--- /dev/null
+++ b/contrib/libarchive/libarchive/test/test_write_disk_appledouble_zip.zip.uu
@@ -0,0 +1,27 @@
+begin 644 test_write_disk_appledouble_zip.zip
+M4$L#!`H```````MM?%@````````````````1`!``87!P;&5?9&]U8FQE7V1I
+M<B]56`P`O=4%9K75!6;U`10`4$L#!`H```````MM?%@````````````````G
+M`!``87!P;&5?9&]U8FQE7V1I<B]A<'!L95]D;W5B;&5?9&ER7W1E<W0O55@,
+M`+W5!6:UU05F]0$4`%!+`P04``@`"``+;7Q8````````````````*``0`&%P
+M<&QE7V1O=6)L95]D:7(O+E]A<'!L95]D;W5B;&5?9&ER7W1E<W156`P`O=4%
+M9K75!6;U`10`8V`58V=@8F#P34Q6\`]6B%"``I`8`R<0&P%Q!1"#^*L8B`*.
+M(2%!4"9(QPP@%D)3PH@0%TW.S]5++"C(2=4K+$TL2LPKR<Q+92C4-S"P,+8V
+M@`)K:P8`4$L'"!2N=6M7````J@```%!+`P04``@`"`!93GQ8````````````
+M````&@`0`&%P<&QE7V1O=6)L95]D:7(O=&5S=%]F:6QE55@,`+'5!6;IGP5F
+M]0$4`"M)+2[A`@!02P<(QC6Y.P<````%````4$L#!!0`"``(`%E.?%@`````
+M```````````<`!``87!P;&5?9&]U8FQE7V1I<B\N7W1E<W1?9FEL9558#`"Q
+MU05FZ9\%9O4!%`!C8!5C9V!B8/!-3%;P#U:(4(`"D!@#)Q`;`;$;$(/X%4#,
+MQT`0.(:$!$&9%5",`8I2B_-+BY)3%=+RB[*Y`%!+!P@HPLP3/@```(8```!0
+M2P$"%0,*```````+;7Q8````````````````$0`,``````````!`[4$`````
+M87!P;&5?9&]U8FQE7V1I<B]56`@`O=4%9K75!6902P$"%0,*```````+;7Q8
+M````````````````)P`,``````````!`[4$_````87!P;&5?9&]U8FQE7V1I
+M<B]A<'!L95]D;W5B;&5?9&ER7W1E<W0O55@(`+W5!6:UU05F4$L!`A4#%``(
+M``@`"VU\6!2N=6M7````J@```"@`#```````````0*2!E````&%P<&QE7V1O
+M=6)L95]D:7(O+E]A<'!L95]D;W5B;&5?9&ER7W1E<W156`@`O=4%9K75!690
+M2P$"%0,4``@`"`!93GQ8QC6Y.P<````%````&@`,``````````!`I(%1`0``
+M87!P;&5?9&]U8FQE7V1I<B]T97-T7V9I;&556`@`L=4%9NF?!6902P$"%0,4
+M``@`"`!93GQ8*,+,$SX```"&````'``,``````````!`I(&P`0``87!P;&5?
+M9&]U8FQE7V1I<B\N7W1E<W1?9FEL9558"`"QU05FZ9\%9E!+!08`````!0`%
++`+@!``!(`@``````
+`
+end
diff --git a/contrib/libarchive/libarchive_fe/passphrase.c b/contrib/libarchive/libarchive_fe/passphrase.c
index 9d95d527067b..90fef32d254c 100644
--- a/contrib/libarchive/libarchive_fe/passphrase.c
+++ b/contrib/libarchive/libarchive_fe/passphrase.c
@@ -76,6 +76,7 @@
 
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
+#include <string.h>
 #include <windows.h>
 
 static char *
@@ -113,8 +114,7 @@ readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
 	WriteFile(hStdout, "\r\n", 2, NULL, NULL);
 	buf[rbytes] = '\0';
 	/* Remove trailing carriage return(s). */
-	if (rbytes > 2 && buf[rbytes - 2] == '\r' && buf[rbytes - 1] == '\n')
-		buf[rbytes - 2] = '\0';
+	buf[strcspn(buf, "\r\n")] = '\0';
 
 	return (buf);
 }
diff --git a/contrib/libarchive/tar/bsdtar.1 b/contrib/libarchive/tar/bsdtar.1
index e570d2a48a01..fe9ec9504674 100644
--- a/contrib/libarchive/tar/bsdtar.1
+++ b/contrib/libarchive/tar/bsdtar.1
@@ -23,7 +23,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 1, 2024
+.Dd April 23, 2024
 .Dt TAR 1
 .Os
 .Sh NAME
@@ -644,14 +644,13 @@ A decimal integer from 4 to 7 specifying the lz4 compression block size
 .It Cm lz4:block-dependence
 Use the previous block of the block being compressed for
 a compression dictionary to improve compression ratio.
-.It Cm zstd:compression-level
-A decimal integer specifying the zstd compression level. Supported values depend
+.It Cm zstd:compression-level Ns = Ns Ar N
+A decimal integer specifying the zstd compression level.
+Supported values depend
 on the library version, common values are from 1 to 22.
-.It Cm zstd:threads
-Specify the number of worker threads to use.
-Setting threads to a special value 0 makes
-.Xr zstd 1
-use as many threads as there are CPU cores on the system.
+.It Cm zstd:threads Ns = Ns Ar N
+Specify the number of worker threads to use, or 0 to use as many
+threads as there are CPU cores in the system.
 .It Cm zstd:frame-per-file
 Start a new compression frame at the beginning of each file in the
 archive.
diff --git a/contrib/libarchive/tar/bsdtar.c b/contrib/libarchive/tar/bsdtar.c
index b070e0faeb66..42baab2861bd 100644
--- a/contrib/libarchive/tar/bsdtar.c
+++ b/contrib/libarchive/tar/bsdtar.c
@@ -157,6 +157,7 @@ main(int argc, char **argv)
 	char			*tptr, *uptr;
 	char			 possible_help_request;
 	char			 buff[16];
+	long			 l;
 
 	/*
 	 * Use a pointer for consistency, but stack-allocated storage
@@ -301,16 +302,15 @@ main(int argc, char **argv)
 			/* libarchive doesn't need this; just ignore it. */
 			break;
 		case 'b': /* SUSv2 */
-			errno = 0;
 			tptr = NULL;
-			t = (int)strtol(bsdtar->argument, &tptr, 10);
-			if (errno || t <= 0 || t > 8192 ||
+			l = strtol(bsdtar->argument, &tptr, 10);
+			if (l <= 0 || l > 8192L ||
 			    *(bsdtar->argument) == '\0' || tptr == NULL ||
 			    *tptr != '\0') {
 				lafe_errc(1, 0, "Invalid or out of range "
 				    "(1..8192) argument to -b");
 			}
-			bsdtar->bytes_per_block = 512 * t;
+			bsdtar->bytes_per_block = 512 * (int)l;
 			/* Explicit -b forces last block size. */
 			bsdtar->bytes_in_last_block = bsdtar->bytes_per_block;
 			break;
@@ -369,44 +369,42 @@ main(int argc, char **argv)
*** 263 LINES SKIPPED ***