diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # libarchive
 
+# 3.0.3.2
+
+  * Bundled libarchive 3.5.3
+
 # 3.0.3.1
 
   * Properly export functions from libarchive 3.5.2.
diff --git a/c/archive.h b/c/archive.h
--- a/c/archive.h
+++ b/c/archive.h
@@ -36,7 +36,7 @@
  * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
  */
 /* Note: Compiler will complain if this does not match archive_entry.h! */
-#define	ARCHIVE_VERSION_NUMBER 3005002
+#define	ARCHIVE_VERSION_NUMBER 3005003
 
 #include <sys/stat.h>
 #include <stddef.h>  /* for wchar_t */
@@ -155,7 +155,7 @@
 /*
  * Textual name/version of the library, useful for version displays.
  */
-#define	ARCHIVE_VERSION_ONLY_STRING "3.5.2"
+#define	ARCHIVE_VERSION_ONLY_STRING "3.5.3"
 #define	ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
 __LA_DECL const char *	archive_version_string(void);
 
diff --git a/c/archive_entry.h b/c/archive_entry.h
--- a/c/archive_entry.h
+++ b/c/archive_entry.h
@@ -30,7 +30,7 @@
 #define	ARCHIVE_ENTRY_H_INCLUDED
 
 /* Note: Compiler will complain if this does not match archive.h! */
-#define	ARCHIVE_VERSION_NUMBER 3005002
+#define	ARCHIVE_VERSION_NUMBER 3005003
 
 /*
  * Note: archive_entry.h is for use outside of libarchive; the
diff --git a/c/archive_read_support_format_rar5.c b/c/archive_read_support_format_rar5.c
--- a/c/archive_read_support_format_rar5.c
+++ b/c/archive_read_support_format_rar5.c
@@ -1012,7 +1012,16 @@
 	return ret;
 }
 
-static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) {
+static int read_bits_32(struct archive_read* a, struct rar5* rar,
+	const uint8_t* p, uint32_t* value)
+{
+	if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_PROGRAMMER,
+			"Premature end of stream during extraction of data (#1)");
+		return ARCHIVE_FATAL;
+	}
+
 	uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24;
 	bits |= p[rar->bits.in_addr + 1] << 16;
 	bits |= p[rar->bits.in_addr + 2] << 8;
@@ -1023,7 +1032,16 @@
 	return ARCHIVE_OK;
 }
 
-static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) {
+static int read_bits_16(struct archive_read* a, struct rar5* rar,
+	const uint8_t* p, uint16_t* value)
+{
+	if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_PROGRAMMER,
+			"Premature end of stream during extraction of data (#2)");
+		return ARCHIVE_FATAL;
+	}
+
 	int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16;
 	bits |= (int) p[rar->bits.in_addr + 1] << 8;
 	bits |= (int) p[rar->bits.in_addr + 2];
@@ -1039,8 +1057,8 @@
 }
 
 /* n = up to 16 */
-static int read_consume_bits(struct rar5* rar, const uint8_t* p, int n,
-    int* value)
+static int read_consume_bits(struct archive_read* a, struct rar5* rar,
+	const uint8_t* p, int n, int* value)
 {
 	uint16_t v;
 	int ret, num;
@@ -1051,7 +1069,7 @@
 		return ARCHIVE_FATAL;
 	}
 
-	ret = read_bits_16(rar, p, &v);
+	ret = read_bits_16(a, rar, p, &v);
 	if(ret != ARCHIVE_OK)
 		return ret;
 
@@ -1712,14 +1730,29 @@
 		}
 	}
 
-	/* If we're currently switching volumes, ignore the new definition of
-	 * window_size. */
-	if(rar->cstate.switch_multivolume == 0) {
-		/* Values up to 64M should fit into ssize_t on every
-		 * architecture. */
-		rar->cstate.window_size = (ssize_t) window_size;
+	if(rar->cstate.window_size < (ssize_t) window_size &&
+	    rar->cstate.window_buf)
+	{
+		/* If window_buf has been allocated before, reallocate it, so
+		 * that its size will match new window_size. */
+
+		uint8_t* new_window_buf =
+			realloc(rar->cstate.window_buf, window_size);
+
+		if(!new_window_buf) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+				"Not enough memory when trying to realloc the window "
+				"buffer.");
+			return ARCHIVE_FATAL;
+		}
+
+		rar->cstate.window_buf = new_window_buf;
 	}
 
+	/* Values up to 64M should fit into ssize_t on every
+	 * architecture. */
+	rar->cstate.window_size = (ssize_t) window_size;
+
 	if(rar->file.solid > 0 && rar->file.solid_window_size == 0) {
 		/* Solid files have to have the same window_size across
 		   whole archive. Remember the window_size parameter
@@ -2425,13 +2458,13 @@
 static int decode_number(struct archive_read* a, struct decode_table* table,
     const uint8_t* p, uint16_t* num)
 {
-	int i, bits, dist;
+	int i, bits, dist, ret;
 	uint16_t bitfield;
 	uint32_t pos;
 	struct rar5* rar = get_context(a);
 
-	if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) {
-		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &bitfield))) {
+		return ret;
 	}
 
 	bitfield &= 0xfffe;
@@ -2537,14 +2570,6 @@
 	for(i = 0; i < HUFF_TABLE_SIZE;) {
 		uint16_t num;
 
-		if((rar->bits.in_addr + 6) >= rar->cstate.cur_block_size) {
-			/* Truncated data, can't continue. */
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Truncated data in huffman tables (#2)");
-			return ARCHIVE_FATAL;
-		}
-
 		ret = decode_number(a, &rar->cstate.bd, p, &num);
 		if(ret != ARCHIVE_OK) {
 			archive_set_error(&a->archive,
@@ -2561,8 +2586,8 @@
 			/* 16..17: repeat previous code */
 			uint16_t n;
 
-			if(ARCHIVE_OK != read_bits_16(rar, p, &n))
-				return ARCHIVE_EOF;
+			if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n)))
+				return ret;
 
 			if(num == 16) {
 				n >>= 13;
@@ -2590,8 +2615,8 @@
 			/* other codes: fill with zeroes `n` times */
 			uint16_t n;
 
-			if(ARCHIVE_OK != read_bits_16(rar, p, &n))
-				return ARCHIVE_EOF;
+			if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n)))
+				return ret;
 
 			if(num == 18) {
 				n >>= 13;
@@ -2707,22 +2732,22 @@
 }
 
 /* Convenience function used during filter processing. */
-static int parse_filter_data(struct rar5* rar, const uint8_t* p,
-    uint32_t* filter_data)
+static int parse_filter_data(struct archive_read* a, struct rar5* rar,
+	const uint8_t* p, uint32_t* filter_data)
 {
-	int i, bytes;
+	int i, bytes, ret;
 	uint32_t data = 0;
 
-	if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes))
-		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != (ret = read_consume_bits(a, rar, p, 2, &bytes)))
+		return ret;
 
 	bytes++;
 
 	for(i = 0; i < bytes; i++) {
 		uint16_t byte;
 
-		if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) {
-			return ARCHIVE_EOF;
+		if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &byte))) {
+			return ret;
 		}
 
 		/* Cast to uint32_t will ensure the shift operation will not
@@ -2765,16 +2790,17 @@
 	uint16_t filter_type;
 	struct filter_info* filt = NULL;
 	struct rar5* rar = get_context(ar);
+	int ret;
 
 	/* Read the parameters from the input stream. */
-	if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start))
-		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_start)))
+		return ret;
 
-	if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length))
-		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_length)))
+		return ret;
 
-	if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type))
-		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != (ret = read_bits_16(ar, rar, p, &filter_type)))
+		return ret;
 
 	filter_type >>= 13;
 	skip_bits(rar, 3);
@@ -2814,8 +2840,8 @@
 	if(filter_type == FILTER_DELTA) {
 		int channels;
 
-		if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels))
-			return ARCHIVE_EOF;
+		if(ARCHIVE_OK != (ret = read_consume_bits(ar, rar, p, 5, &channels)))
+			return ret;
 
 		filt->channels = channels + 1;
 	}
@@ -2823,10 +2849,11 @@
 	return ARCHIVE_OK;
 }
 
-static int decode_code_length(struct rar5* rar, const uint8_t* p,
-    uint16_t code)
+static int decode_code_length(struct archive_read* a, struct rar5* rar,
+	const uint8_t* p, uint16_t code)
 {
 	int lbits, length = 2;
+
 	if(code < 8) {
 		lbits = 0;
 		length += code;
@@ -2838,7 +2865,7 @@
 	if(lbits > 0) {
 		int add;
 
-		if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add))
+		if(ARCHIVE_OK != read_consume_bits(a, rar, p, lbits, &add))
 			return -1;
 
 		length += add;
@@ -2933,7 +2960,7 @@
 			continue;
 		} else if(num >= 262) {
 			uint16_t dist_slot;
-			int len = decode_code_length(rar, p, num - 262),
+			int len = decode_code_length(a, rar, p, num - 262),
 				dbits,
 				dist = 1;
 
@@ -2975,12 +3002,12 @@
 					uint16_t low_dist;
 
 					if(dbits > 4) {
-						if(ARCHIVE_OK != read_bits_32(
-						    rar, p, &add)) {
+						if(ARCHIVE_OK != (ret = read_bits_32(
+						    a, rar, p, &add))) {
 							/* Return EOF if we
 							 * can't read more
 							 * data. */
-							return ARCHIVE_EOF;
+							return ret;
 						}
 
 						skip_bits(rar, dbits - 4);
@@ -3015,11 +3042,11 @@
 					/* dbits is one of [0,1,2,3] */
 					int add;
 
-					if(ARCHIVE_OK != read_consume_bits(rar,
-					     p, dbits, &add)) {
+					if(ARCHIVE_OK != (ret = read_consume_bits(a, rar,
+					     p, dbits, &add))) {
 						/* Return EOF if we can't read
 						 * more data. */
-						return ARCHIVE_EOF;
+						return ret;
 					}
 
 					dist += add;
@@ -3076,7 +3103,11 @@
 				return ARCHIVE_FATAL;
 			}
 
-			len = decode_code_length(rar, p, len_slot);
+			len = decode_code_length(a, rar, p, len_slot);
+			if (len == -1) {
+				return ARCHIVE_FATAL;
+			}
+
 			rar->cstate.last_len = len;
 
 			if(ARCHIVE_OK != copy_string(a, len, dist))
@@ -3598,6 +3629,16 @@
 		}
 
 		rar->cstate.initialized = 1;
+	}
+
+	/* Don't allow extraction if window_size is invalid. */
+	if(rar->cstate.window_size == 0) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_FILE_FORMAT,
+			"Invalid window size declaration in this file");
+
+		/* This should never happen in valid files. */
+		return ARCHIVE_FATAL;
 	}
 
 	if(rar->cstate.all_filters_applied == 1) {
diff --git a/c/archive_write_disk_posix.c b/c/archive_write_disk_posix.c
--- a/c/archive_write_disk_posix.c
+++ b/c/archive_write_disk_posix.c
@@ -173,6 +173,7 @@
 	struct fixup_entry	*next;
 	struct archive_acl	 acl;
 	mode_t			 mode;
+	__LA_MODE_T		 filetype;
 	int64_t			 atime;
 	int64_t                  birthtime;
 	int64_t			 mtime;
@@ -357,6 +358,7 @@
 
 static int	la_opendirat(int, const char *);
 static int	la_mktemp(struct archive_write_disk *);
+static int	la_verify_filetype(mode_t, __LA_MODE_T);
 static void	fsobj_error(int *, struct archive_string *, int, const char *,
 		    const char *);
 static int	check_symlinks_fsobj(char *, int *, struct archive_string *,
@@ -465,6 +467,39 @@
 }
 
 static int
+la_verify_filetype(mode_t mode, __LA_MODE_T filetype) {
+	int ret = 0;
+
+	switch (filetype) {
+	case AE_IFREG:
+		ret = (S_ISREG(mode));
+		break;
+	case AE_IFDIR:
+		ret = (S_ISDIR(mode));
+		break;
+	case AE_IFLNK:
+		ret = (S_ISLNK(mode));
+		break;
+	case AE_IFSOCK:
+		ret = (S_ISSOCK(mode));
+		break;
+	case AE_IFCHR:
+		ret = (S_ISCHR(mode));
+		break;
+	case AE_IFBLK:
+		ret = (S_ISBLK(mode));
+		break;
+	case AE_IFIFO:
+		ret = (S_ISFIFO(mode));
+		break;
+	default:
+		break;
+	}
+
+	return (ret);
+}
+
+static int
 lazy_stat(struct archive_write_disk *a)
 {
 	if (a->pst != NULL) {
@@ -822,6 +857,7 @@
 		fe = current_fixup(a, archive_entry_pathname(entry));
 		if (fe == NULL)
 			return (ARCHIVE_FATAL);
+		fe->filetype = archive_entry_filetype(entry);
 		fe->fixup |= TODO_MODE_BASE;
 		fe->mode = a->mode;
 	}
@@ -832,6 +868,7 @@
 		fe = current_fixup(a, archive_entry_pathname(entry));
 		if (fe == NULL)
 			return (ARCHIVE_FATAL);
+		fe->filetype = archive_entry_filetype(entry);
 		fe->mode = a->mode;
 		fe->fixup |= TODO_TIMES;
 		if (archive_entry_atime_is_set(entry)) {
@@ -865,6 +902,7 @@
 		fe = current_fixup(a, archive_entry_pathname(entry));
 		if (fe == NULL)
 			return (ARCHIVE_FATAL);
+		fe->filetype = archive_entry_filetype(entry);
 		fe->fixup |= TODO_ACLS;
 		archive_acl_copy(&fe->acl, archive_entry_acl(entry));
 	}
@@ -877,6 +915,7 @@
 			fe = current_fixup(a, archive_entry_pathname(entry));
 			if (fe == NULL)
 				return (ARCHIVE_FATAL);
+			fe->filetype = archive_entry_filetype(entry);
 			fe->mac_metadata = malloc(metadata_size);
 			if (fe->mac_metadata != NULL) {
 				memcpy(fe->mac_metadata, metadata,
@@ -891,6 +930,7 @@
 		fe = current_fixup(a, archive_entry_pathname(entry));
 		if (fe == NULL)
 			return (ARCHIVE_FATAL);
+		fe->filetype = archive_entry_filetype(entry);
 		fe->fixup |= TODO_FFLAGS;
 		/* TODO: Complete this.. defer fflags from below. */
 	}
@@ -2462,7 +2502,8 @@
 	struct archive_write_disk *a = (struct archive_write_disk *)_a;
 	struct fixup_entry *next, *p;
 	struct stat st;
-	int fd, ret;
+	char *c;
+	int fd, ret, openflags;
 
 	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
 	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
@@ -2475,24 +2516,70 @@
 	while (p != NULL) {
 		fd = -1;
 		a->pst = NULL; /* Mark stat cache as out-of-date. */
-		if (p->fixup &
-		    (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) {
-			fd = open(p->name,
-			    O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC);
-			if (fd == -1) {
-				/* If we cannot lstat, skip entry */
-				if (lstat(p->name, &st) != 0)
+
+		/* We must strip trailing slashes from the path to avoid
+		   dereferencing symbolic links to directories */
+		c = p->name;
+		while (*c != '\0')
+			c++;
+		while (c != p->name && *(c - 1) == '/') {
+			c--;
+			*c = '\0';
+		}
+
+		if (p->fixup == 0)
+			goto skip_fixup_entry;
+		else {
+			/*
+			 * We need to verify if the type of the file
+			 * we are going to open matches the file type
+			 * of the fixup entry.
+			 */
+			openflags = O_BINARY | O_NOFOLLOW | O_RDONLY
+			    | O_CLOEXEC;
+#if defined(O_DIRECTORY)
+			if (p->filetype == AE_IFDIR)
+				openflags |= O_DIRECTORY;
+#endif
+			fd = open(p->name, openflags);
+
+#if defined(O_DIRECTORY)
+			/*
+			 * If we support O_DIRECTORY and open was
+			 * successful we can skip the file type check
+			 * for directories. For other file types
+			 * we need to verify via fstat() or lstat()
+			 */
+			if (fd == -1 || p->filetype != AE_IFDIR) {
+#if HAVE_FSTAT
+				if (fd > 0 && (
+				    fstat(fd, &st) != 0 ||
+				    la_verify_filetype(st.st_mode,
+				    p->filetype) == 0)) {
 					goto skip_fixup_entry;
-				/*
-				 * If we deal with a symbolic link, mark
-				 * it in the fixup mode to ensure no
-				 * modifications are made to its target.
-				 */
-				if (S_ISLNK(st.st_mode)) {
-					p->mode &= ~S_IFMT;
-					p->mode |= S_IFLNK;
+				} else
+#endif
+				if (lstat(p->name, &st) != 0 ||
+				    la_verify_filetype(st.st_mode,
+				    p->filetype) == 0) {
+					goto skip_fixup_entry;
 				}
 			}
+#else
+#if HAVE_FSTAT
+			if (fd > 0 && (
+			    fstat(fd, &st) != 0 ||
+			    la_verify_filetype(st.st_mode,
+			    p->filetype) == 0)) {
+				goto skip_fixup_entry;
+			} else
+#endif
+			if (lstat(p->name, &st) != 0 ||
+			    la_verify_filetype(st.st_mode,
+			    p->filetype) == 0) {
+				goto skip_fixup_entry;
+			}
+#endif
 		}
 		if (p->fixup & TODO_TIMES) {
 			set_times(a, fd, p->mode, p->name,
@@ -2504,14 +2591,13 @@
 		if (p->fixup & TODO_MODE_BASE) {
 #ifdef HAVE_FCHMOD
 			if (fd >= 0)
-				fchmod(fd, p->mode);
+				fchmod(fd, p->mode & 07777);
 			else
 #endif
 #ifdef HAVE_LCHMOD
-			lchmod(p->name, p->mode);
+			lchmod(p->name, p->mode & 07777);
 #else
-			if (!S_ISLNK(p->mode))
-				chmod(p->name, p->mode);
+			chmod(p->name, p->mode & 07777);
 #endif
 		}
 		if (p->fixup & TODO_ACLS)
@@ -2664,7 +2750,7 @@
 	fe->next = a->fixup_list;
 	a->fixup_list = fe;
 	fe->fixup = 0;
-	fe->mode = 0;
+	fe->filetype = 0;
 	fe->name = strdup(pathname);
 	return (fe);
 }
@@ -3787,6 +3873,7 @@
 			le = current_fixup(a, a->name);
 			if (le == NULL)
 				return (ARCHIVE_FATAL);
+			le->filetype = archive_entry_filetype(a->entry);
 			le->fixup |= TODO_FFLAGS;
 			le->fflags_set = set;
 			/* Store the mode if it's not already there. */
diff --git a/c/autoconf-darwin/config.h b/c/autoconf-darwin/config.h
--- a/c/autoconf-darwin/config.h
+++ b/c/autoconf-darwin/config.h
@@ -170,13 +170,13 @@
 /* #undef ARCHIVE_XATTR_LINUX */
 
 /* Version number of bsdcat */
-#define BSDCAT_VERSION_STRING "3.5.2"
+#define BSDCAT_VERSION_STRING "3.5.3"
 
 /* Version number of bsdcpio */
-#define BSDCPIO_VERSION_STRING "3.5.2"
+#define BSDCPIO_VERSION_STRING "3.5.3"
 
 /* Version number of bsdtar */
-#define BSDTAR_VERSION_STRING "3.5.2"
+#define BSDTAR_VERSION_STRING "3.5.3"
 
 /* Define to 1 if the system has the type `ace_t'. */
 /* #undef HAVE_ACE_T */
@@ -1248,10 +1248,10 @@
 /* #undef ICONV_CONST */
 
 /* Version number of libarchive as a single integer */
-#define LIBARCHIVE_VERSION_NUMBER "3005002"
+#define LIBARCHIVE_VERSION_NUMBER "3005003"
 
 /* Version number of libarchive */
-#define LIBARCHIVE_VERSION_STRING "3.5.2"
+#define LIBARCHIVE_VERSION_STRING "3.5.3"
 
 /* Define to 1 if `lstat' dereferences a symlink specified with a trailing
    slash. */
@@ -1281,7 +1281,7 @@
 #define PACKAGE_NAME "libarchive"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "libarchive 3.5.2"
+#define PACKAGE_STRING "libarchive 3.5.3"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "libarchive"
@@ -1290,7 +1290,7 @@
 #define PACKAGE_URL ""
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "3.5.2"
+#define PACKAGE_VERSION "3.5.3"
 
 /* Define to 1 if PCRE_STATIC needs to be defined. */
 /* #undef PCRE_STATIC */
@@ -1330,7 +1330,7 @@
 
 
 /* Version number of package */
-#define VERSION "3.5.2"
+#define VERSION "3.5.3"
 
 /* Define to '0x0502' for Windows Server 2003 APIs. */
 /* #undef WINVER */
diff --git a/c/autoconf-freebsd/config.h b/c/autoconf-freebsd/config.h
--- a/c/autoconf-freebsd/config.h
+++ b/c/autoconf-freebsd/config.h
@@ -165,13 +165,13 @@
 /* #undef ARCHIVE_XATTR_LINUX */
 
 /* Version number of bsdcat */
-#define BSDCAT_VERSION_STRING "3.5.2"
+#define BSDCAT_VERSION_STRING "3.5.3"
 
 /* Version number of bsdcpio */
-#define BSDCPIO_VERSION_STRING "3.5.2"
+#define BSDCPIO_VERSION_STRING "3.5.3"
 
 /* Version number of bsdtar */
-#define BSDTAR_VERSION_STRING "3.5.2"
+#define BSDTAR_VERSION_STRING "3.5.3"
 
 /* Define to 1 if the system has the type `ace_t'. */
 /* #undef HAVE_ACE_T */
@@ -1237,10 +1237,10 @@
 /* #undef ICONV_CONST */
 
 /* Version number of libarchive as a single integer */
-#define LIBARCHIVE_VERSION_NUMBER "3005002"
+#define LIBARCHIVE_VERSION_NUMBER "3005003"
 
 /* Version number of libarchive */
-#define LIBARCHIVE_VERSION_STRING "3.5.2"
+#define LIBARCHIVE_VERSION_STRING "3.5.3"
 
 /* Define to 1 if `lstat' dereferences a symlink specified with a trailing
    slash. */
@@ -1270,7 +1270,7 @@
 #define PACKAGE_NAME "libarchive"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "libarchive 3.5.2"
+#define PACKAGE_STRING "libarchive 3.5.3"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "libarchive"
@@ -1279,7 +1279,7 @@
 #define PACKAGE_URL ""
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "3.5.2"
+#define PACKAGE_VERSION "3.5.3"
 
 /* Define to 1 if PCRE_STATIC needs to be defined. */
 /* #undef PCRE_STATIC */
@@ -1319,7 +1319,7 @@
 
 
 /* Version number of package */
-#define VERSION "3.5.2"
+#define VERSION "3.5.3"
 
 /* Define to '0x0502' for Windows Server 2003 APIs. */
 /* #undef WINVER */
diff --git a/c/autoconf-linux/config.h b/c/autoconf-linux/config.h
--- a/c/autoconf-linux/config.h
+++ b/c/autoconf-linux/config.h
@@ -170,13 +170,13 @@
 /* #undef ARCHIVE_XATTR_LINUX */
 
 /* Version number of bsdcat */
-#define BSDCAT_VERSION_STRING "3.5.2"
+#define BSDCAT_VERSION_STRING "3.5.3"
 
 /* Version number of bsdcpio */
-#define BSDCPIO_VERSION_STRING "3.5.2"
+#define BSDCPIO_VERSION_STRING "3.5.3"
 
 /* Version number of bsdtar */
-#define BSDTAR_VERSION_STRING "3.5.2"
+#define BSDTAR_VERSION_STRING "3.5.3"
 
 /* Define to 1 if the system has the type `ace_t'. */
 /* #undef HAVE_ACE_T */
@@ -1248,10 +1248,10 @@
 /* #undef ICONV_CONST */
 
 /* Version number of libarchive as a single integer */
-#define LIBARCHIVE_VERSION_NUMBER "3005002"
+#define LIBARCHIVE_VERSION_NUMBER "3005003"
 
 /* Version number of libarchive */
-#define LIBARCHIVE_VERSION_STRING "3.5.2"
+#define LIBARCHIVE_VERSION_STRING "3.5.3"
 
 /* Define to 1 if `lstat' dereferences a symlink specified with a trailing
    slash. */
@@ -1281,7 +1281,7 @@
 #define PACKAGE_NAME "libarchive"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "libarchive 3.5.2"
+#define PACKAGE_STRING "libarchive 3.5.3"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "libarchive"
@@ -1290,7 +1290,7 @@
 #define PACKAGE_URL ""
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "3.5.2"
+#define PACKAGE_VERSION "3.5.3"
 
 /* Define to 1 if PCRE_STATIC needs to be defined. */
 /* #undef PCRE_STATIC */
@@ -1330,7 +1330,7 @@
 
 
 /* Version number of package */
-#define VERSION "3.5.2"
+#define VERSION "3.5.3"
 
 /* Define to '0x0502' for Windows Server 2003 APIs. */
 /* #undef WINVER */
diff --git a/c/autoconf-windows/config.h b/c/autoconf-windows/config.h
--- a/c/autoconf-windows/config.h
+++ b/c/autoconf-windows/config.h
@@ -170,13 +170,13 @@
 /* #undef ARCHIVE_XATTR_LINUX */
 
 /* Version number of bsdcat */
-#define BSDCAT_VERSION_STRING "3.5.2"
+#define BSDCAT_VERSION_STRING "3.5.3"
 
 /* Version number of bsdcpio */
-#define BSDCPIO_VERSION_STRING "3.5.2"
+#define BSDCPIO_VERSION_STRING "3.5.3"
 
 /* Version number of bsdtar */
-#define BSDTAR_VERSION_STRING "3.5.2"
+#define BSDTAR_VERSION_STRING "3.5.3"
 
 /* Define to 1 if the system has the type `ace_t'. */
 /* #undef HAVE_ACE_T */
@@ -1240,10 +1240,10 @@
 /* #undef ICONV_CONST */
 
 /* Version number of libarchive as a single integer */
-#define LIBARCHIVE_VERSION_NUMBER "3005002"
+#define LIBARCHIVE_VERSION_NUMBER "3005003"
 
 /* Version number of libarchive */
-#define LIBARCHIVE_VERSION_STRING "3.5.2"
+#define LIBARCHIVE_VERSION_STRING "3.5.3"
 
 /* Define to 1 if `lstat' dereferences a symlink specified with a trailing
    slash. */
@@ -1273,7 +1273,7 @@
 #define PACKAGE_NAME "libarchive"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "libarchive 3.5.2"
+#define PACKAGE_STRING "libarchive 3.5.3"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "libarchive"
@@ -1282,7 +1282,7 @@
 #define PACKAGE_URL ""
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "3.5.2"
+#define PACKAGE_VERSION "3.5.3"
 
 /* Define to 1 if PCRE_STATIC needs to be defined. */
 /* #undef PCRE_STATIC */
@@ -1322,7 +1322,7 @@
 
 
 /* Version number of package */
-#define VERSION "3.5.2"
+#define VERSION "3.5.3"
 
 /* Define to '0x0502' for Windows Server 2003 APIs. */
 #define WINVER 0x0502
diff --git a/exe/Main.hs b/exe/Main.hs
new file mode 100644
--- /dev/null
+++ b/exe/Main.hs
@@ -0,0 +1,247 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module Main where
+
+import           Codec.Archive                 hiding (unpackToDir)
+import           Control.Monad                 (forM_)
+import           Control.Monad.Catch           (Exception, MonadThrow, throwM)
+import           Data.Char                     (toUpper)
+import           Data.Bits                     (testBit)
+import           Data.List                     (isSuffixOf)
+import           System.IO                     (stderr, hPutStrLn)
+import           System.Directory              (getCurrentDirectory, setCurrentDirectory)
+import           System.Console.GetOpt         (OptDescr(..), ArgDescr(..), ArgOrder(..),
+                                               getOpt', usageInfo)
+import           System.Environment            (getArgs)
+import           System.Exit                   (exitFailure)
+import           Data.Time                     (formatTime)
+import           Data.Time.Clock.POSIX         (posixSecondsToUTCTime, POSIXTime)
+import           Data.Time                     (defaultTimeLocale)
+
+import qualified Codec.Compression.BZip        as BZip
+import qualified Codec.Compression.GZip        as GZip
+import qualified Codec.Compression.Lzma        as Lzma
+import qualified Data.ByteString.Lazy          as BL
+import qualified Data.ByteString               as BS
+
+
+throwEither :: (Exception a, MonadThrow m) => Either a b -> m b
+throwEither a = case a of
+  Left  e -> throwM e
+  Right r -> pure r
+
+throwEitherM :: (Exception a, MonadThrow m) => m (Either a b) -> m b
+throwEitherM a = a >>= throwEither
+
+
+data Compression = None | GZip | BZip | XZ
+  deriving (Show, Eq)
+
+compressionFromFileName :: FilePath -> Compression
+compressionFromFileName fn
+  | ".tar.gz"  `isSuffixOf` fn = GZip
+  | ".tar.xz"  `isSuffixOf` fn = XZ
+  | ".tar.bz2" `isSuffixOf` fn = BZip
+  | otherwise                  = None
+
+compress :: Compression -> BL.ByteString -> BL.ByteString
+compress GZip = GZip.compress
+compress BZip = BZip.compress
+compress XZ   = Lzma.compress
+compress None = id
+
+decompress :: Compression -> BL.ByteString -> BL.ByteString
+decompress GZip = GZip.decompress
+decompress BZip = BZip.decompress
+decompress XZ   = Lzma.decompress
+decompress None = id
+
+data Verbosity = Verbose | Concise
+
+main' :: Options -> [FilePath] -> IO ()
+main' (Options { optFile        = file,
+                 optDir         = dir',
+                 optAction      = action,
+                 optCompression = compression',
+                 optVerbosity   = verbosity }) files =
+  case action of
+    NoAction -> die ["No action given. Specify one of -c, -t or -x."]
+    Help     -> printUsage
+    Create   -> do
+      dir <- getDir
+      setCurrentDirectory dir
+      bs <- packFiles files
+      writeOutput . compress compression $ bs
+    Extract  -> do
+      dir <- getDir
+      input <- getInput
+      throwEitherM . runArchiveM . unpackToDirLazy dir . decompress compression $ input
+    List     -> do
+      input <- getInput
+      entries <- throwEither . readArchiveBSL . decompress compression $ input
+      printEntries entries
+    Append -> die ["Not implemented yet!"]
+  where
+    getInput    = if file == "-" then BL.getContents else BL.readFile  file
+    writeOutput = if file == "-" then BL.putStr      else BL.writeFile file
+    getDir      = if dir' == ""  then getCurrentDirectory else pure dir'
+    compression = case compression' of
+      None -> compressionFromFileName file
+      c    -> c
+    printEntries entries = forM_ entries printEntry
+    printEntry = putStrLn . entryInfo verbosity
+
+
+main :: IO ()
+main = do
+  (opts, files) <- parseOptions =<< getArgs
+  main' opts files
+
+
+------------------------
+-- List archive contents
+
+entryInfo :: Verbosity -> Entry FilePath BS.ByteString -> String
+entryInfo Verbose = detailedInfo
+entryInfo Concise = filepath
+
+detailedInfo :: Entry FilePath BS.ByteString -> String
+detailedInfo Entry{..} =
+  unwords [ typeCode : permissions'
+          , justify 19 (owner ++ '/' : group) size
+          , time'
+          , name ++ link ]
+  where
+    typeCode = case content of
+      Hardlink _  -> 'h'
+      Symlink _ _ -> 'l'
+      Directory   -> 'd'
+      _           -> '-'
+    permissions' = concat [userPerms, groupPerms, otherPerms]
+      where
+        userPerms  = formatPerms 8 7 6 11 's'
+        groupPerms = formatPerms 5 4 3 10 's'
+        otherPerms = formatPerms 2 1 0  9 't'
+        formatPerms r w x s c =
+          [if testBit m r then 'r' else '-'
+          ,if testBit m w then 'w' else '-'
+          ,if testBit m s
+             then if testBit m x then c   else toUpper c
+             else if testBit m x then 'x' else '-']
+        m = permissions
+    owner = nameOrID ownerName ownerId
+    group = nameOrID groupName groupId
+    (Ownership ownerName groupName ownerId groupId) = ownership
+    nameOrID Nothing i  = show i
+    nameOrID (Just n) _ = n
+    size = case content of
+             NormalFile c -> show (BS.length c)
+             _            -> "0"
+
+    time' = maybe "unknown" (formatEpochTime "%Y-%m-%d %H:%M") time
+    name = filepath
+    link = case content of
+      Hardlink     l   -> " link to " ++ l
+      Symlink l _ -> " -> "      ++ l
+      _                -> ""
+
+justify :: Int -> String -> String -> String
+justify width left right = left ++ padding ++ right
+  where
+    padding  = replicate padWidth ' '
+    padWidth = max 1 (width - length left - length right)
+
+formatEpochTime :: String -> ModTime -> String
+formatEpochTime f (t, _) =
+    formatTime defaultTimeLocale f . posixSecondsToUTCTime $ (realToFrac t :: POSIXTime)
+
+
+------------------------
+-- Command line handling (stolen from 'tar')
+
+data Options = Options {
+    optFile        :: FilePath, -- "-" means stdin/stdout
+    optDir         :: FilePath,
+    optAction      :: Action,
+    optCompression :: Compression,
+    optVerbosity   :: Verbosity
+  }
+
+defaultOptions :: Options
+defaultOptions = Options {
+    optFile        = "-",
+    optDir         = "",
+    optAction      = NoAction,
+    optCompression = None,
+    optVerbosity   = Concise
+  }
+
+data Action = NoAction
+            | Help
+            | Create
+            | Extract
+            | List
+            | Append
+  deriving Show
+
+optDescr :: [OptDescr (Options -> Options)]
+optDescr =
+  [ Option ['c'] ["create"]
+      (action Create)
+      "Create a new archive."
+  , Option ['x'] ["extract", "get"]
+      (action Extract)
+      "Extract files from an archive."
+  , Option ['t'] ["list"]
+      (action List)
+      "List the contents of an archive."
+  , Option ['r'] ["append"]
+      (action Append)
+      "Append files to the end of an archive."
+  , Option ['f'] ["file"]
+      (ReqArg (\f o -> o { optFile = f}) "ARCHIVE")
+      "Use archive file ARCHIVE."
+  , Option ['C'] ["directory"]
+      (ReqArg (\d o -> o { optDir = d }) "DIR")
+      "Create or extract relative to DIR."
+  , Option ['z'] ["gzip", "gunzip", "ungzip"]
+      (compression GZip)
+      "Use gzip compression."
+  , Option ['j'] ["bzip2"]
+      (compression BZip)
+      "Use bzip2 compression."
+  , Option ['J'] ["xz"]
+      (compression XZ)
+      "Use xz compression."
+  , Option ['v'] ["verbose"]
+      (NoArg (\o -> o { optVerbosity = Verbose }))
+      "Verbosely list files processed."
+  , Option ['h', '?'] ["help"]
+      (action Help)
+      "Print this help output."
+  ]
+  where
+    action      a = NoArg (\o -> o { optAction = a })
+    compression c = NoArg (\o -> o { optCompression = c })
+
+printUsage :: IO ()
+printUsage = putStrLn (usageInfo headder optDescr)
+  where
+    headder = unlines ["archive creates and extracts TAR archives.",
+                       "",
+                       "Usage: archive [OPTION ...] [FILE ...]"]
+
+parseOptions :: [String] -> IO (Options, [FilePath])
+parseOptions args =
+  let (fs, files, nonopts, errors) = getOpt' Permute optDescr args
+  in case (nonopts, errors) of
+       ([], [])    -> return $ (foldl (flip ($)) defaultOptions fs, files)
+       (_ , (_:_)) -> die errors
+       (_ ,  _)    -> die (map (("unrecognized option "++).show) nonopts)
+
+
+die :: [String] -> IO a
+die errs = do
+  mapM_ (\e -> hPutStrLn stderr $ "archive: " ++ e) $ errs
+  hPutStrLn stderr "Try `archive --help' for more information."
+  exitFailure
diff --git a/libarchive.cabal b/libarchive.cabal
--- a/libarchive.cabal
+++ b/libarchive.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               libarchive
-version:            3.0.3.1
+version:            3.0.3.2
 license:            BSD-3-Clause
 license-file:       LICENSE
 copyright:          Copyright: (c) 2018-2020 Vanessa McHale
@@ -8,7 +8,7 @@
 author:             Vanessa McHale
 tested-with:
     ghc ==7.10.3 ghc ==8.0.2 ghc ==8.2.2 ghc ==8.4.4 ghc ==8.6.5
-    ghc ==8.8.4 ghc ==8.10.7 ghc ==9.0.1
+    ghc ==8.8.4 ghc ==8.10.7 ghc ==9.0.2 ghc ==9.2.1
 
 bug-reports:        https://github.com/vmchale/libarchive/issues
 synopsis:           Haskell interface to libarchive
@@ -55,6 +55,9 @@
     description:
         Use libarchive found with pkg-config rather than the bundled sources
 
+flag no-exe
+    description: don't install the archive executables
+
 library
     exposed-modules:
         Codec.Archive
@@ -285,8 +288,28 @@
                         build-depends: unbuildable <0
 
     else
-        pkgconfig-depends: libarchive (==3.5.0 || >3.5.0) && <4.0
+        pkgconfig-depends: libarchive (==3.5.2 || >3.5.2) && <4.0
 
+executable archive
+    main-is:          Main.hs
+    hs-source-dirs:   exe
+    default-language: Haskell2010
+    ghc-options:      -Wall
+    build-depends:
+        base >=4.8 && <5,
+        bytestring >=0.9,
+        bzlib >=0.4 && <0.7,
+        directory >=1.0,
+        exceptions,
+        filepath >=1.0,
+        libarchive,
+        lzma-static,
+        time,
+        zlib ^>=0.6.2.2
+
+    if flag(no-exe)
+        buildable: False
+
 test-suite libarchive-test
     type:               exitcode-stdio-1.0
     main-is:            Spec.hs
@@ -304,7 +327,6 @@
         deepseq,
         libarchive,
         hspec,
-        hspec-core,
         bytestring,
         directory >=1.2.5.0,
         filepath,
