Set the ZIP version in the Central File Directory to UNIX

- Correctly set ZIP version in the Central File Directory so that the external_attributes field is correctly interpreted as a unix file mode
- Add unit tests to verify permissions are maintained

Fixes #2074

Closes #6375.

PiperOrigin-RevId: 218493221
diff --git a/third_party/ijar/test/zip_test.sh b/third_party/ijar/test/zip_test.sh
index d70fa8f..a938556 100755
--- a/third_party/ijar/test/zip_test.sh
+++ b/third_party/ijar/test/zip_test.sh
@@ -188,4 +188,27 @@
       || fail "Unzip after zipper output is not expected"
 }
 
+function test_zipper_permissions() {
+  local -r LOCAL_TEST_DIR="${TEST_TMPDIR}/${FUNCNAME[0]}"
+  mkdir -p ${LOCAL_TEST_DIR}/files
+  printf "#!/bin/sh\nexit 0\n" > ${LOCAL_TEST_DIR}/files/executable
+  printf "#!/bin/sh\nexit 0\n" > ${LOCAL_TEST_DIR}/files/non_executable
+  chmod +x ${LOCAL_TEST_DIR}/files/executable
+  chmod -x ${LOCAL_TEST_DIR}/files/non_executable
+
+  ${ZIPPER} cC ${LOCAL_TEST_DIR}/output.zip \
+      executable=${LOCAL_TEST_DIR}/files/executable \
+      non_executable=${LOCAL_TEST_DIR}/files/non_executable
+
+  mkdir -p ${LOCAL_TEST_DIR}/out
+  cd ${LOCAL_TEST_DIR}/out && $UNZIP -q ${LOCAL_TEST_DIR}/output.zip
+
+  if ! test -x ${LOCAL_TEST_DIR}/out/executable; then
+    fail "out/executable should have been executable"
+  fi
+  if test -x ${LOCAL_TEST_DIR}/out/non_executable; then
+    fail "out/non_executable should not have been executable"
+  fi
+}
+
 run_suite "zipper tests"
diff --git a/third_party/ijar/zip.cc b/third_party/ijar/zip.cc
index 3bc66b5..ec29bed 100644
--- a/third_party/ijar/zip.cc
+++ b/third_party/ijar/zip.cc
@@ -41,6 +41,7 @@
 
 #define LOCAL_FILE_HEADER_SIGNATURE   0x04034b50
 #define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50
+#define UNIX_ZIP_FILE_VERSION 0x0300
 #define DIGITAL_SIGNATURE             0x05054b50
 #define ZIP64_EOCD_SIGNATURE          0x06064b50
 #define ZIP64_EOCD_LOCATOR_SIGNATURE  0x07064b50
@@ -893,7 +894,7 @@
   for (size_t ii = 0; ii < entries_.size(); ++ii) {
     LocalFileEntry *entry = entries_[ii];
     put_u4le(q, CENTRAL_FILE_HEADER_SIGNATURE);
-    put_u2le(q, 0);  // version made by
+    put_u2le(q, UNIX_ZIP_FILE_VERSION);
 
     put_u2le(q, ZIP_VERSION_TO_EXTRACT);  // version to extract
     put_u2le(q, 0);  // general purpose bit flag
@@ -924,7 +925,7 @@
     put_u4le(q, ZIP64_EOCD_SIGNATURE);
     // signature and size field doesn't count towards size
     put_u8le(q, ZIP64_EOCD_FIXED_SIZE - 12);
-    put_u2le(q, 0);  // version made by
+    put_u2le(q, UNIX_ZIP_FILE_VERSION);  // version made by
     put_u2le(q, 0);  // version needed to extract
     put_u4le(q, 0);  // number of this disk
     put_u4le(q, 0);  // # of the disk with the start of the central directory