{"thread":{"id":"5845","subject":"[PATCH 2/2] git-archive --format=zip: add symlink support","startedAt":"2006-10-06T23:47:35Z","lastAt":"2006-10-06T23:47:35Z","messageCount":1,"participants":["Rene Scharfe"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"28333","messageId":"4526EB17.1010309@lsrfire.ath.cx","threadId":"5845","inReplyTo":null,"subject":"[PATCH 2/2] git-archive --format=zip: add symlink support","fromName":"Rene Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2006-10-06T23:47:35Z","receivedAt":"2006-10-06T23:47:35Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Add symlink support to ZIP file creation, and a few tests.\n\nThis implementation sets the \"version made by\" field\n(creator_version) to Unix for symlinks, only; regular files and\ndirectories are still marked as originating from FAT/VFAT/NTFS.\n\nAlso set \"external file attributes\" (attr2) to 0 for regular\nfiles and 16 for directories (FAT attribute), and to the file\nmode for symlinks.\n\nWe could always set the creator_version to Unix and include the\nmode, but then Info-ZIP unzip would set the mode of the extracted\nfiles to *exactly* the value stored in attr2.  The FAT trick\nmakes it apply the umask instead.  Note: FAT has no executable\nbit, so this information is not stored in the ZIP file.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n\nI tested this change only using Info-ZIP unzip 5.52 on Linux.  I\nwonder what PKZIP or WinZIP would do with symlink entries.\n\nAll calls of git-tar-tree in the test script should be converted\nto git-archive --format=tar, then the script can be renamed to\nt5000-archive.sh.  Later.\n\n archive-zip.c       |   13 +++++++++----\n t/t5000-tar-tree.sh |   35 +++++++++++++++++++++++++++++++++++\n 2 files changed, 44 insertions(+), 4 deletions(-)\n\ndiff --git a/archive-zip.c b/archive-zip.c\nindex ae74623..d8317f8 100644\n--- a/archive-zip.c\n+++ b/archive-zip.c\n@@ -145,6 +145,7 @@ static int write_zip_entry(const unsigne\n {\n \tstruct zip_local_header header;\n \tstruct zip_dir_header dirent;\n+\tunsigned long attr2;\n \tunsigned long compressed_size;\n \tunsigned long uncompressed_size;\n \tunsigned long crc;\n@@ -172,12 +173,16 @@ static int write_zip_entry(const unsigne\n \n \tif (S_ISDIR(mode)) {\n \t\tmethod = 0;\n+\t\tattr2 = 16;\n \t\tresult = READ_TREE_RECURSIVE;\n \t\tout = NULL;\n \t\tuncompressed_size = 0;\n \t\tcompressed_size = 0;\n-\t} else if (S_ISREG(mode)) {\n-\t\tmethod = zlib_compression_level == 0 ? 0 : 8;\n+\t} else if (S_ISREG(mode) || S_ISLNK(mode)) {\n+\t\tmethod = 0;\n+\t\tattr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) : 0;\n+\t\tif (S_ISREG(mode) && zlib_compression_level != 0)\n+\t\t\tmethod = 8;\n \t\tresult = 0;\n \t\tbuffer = read_sha1_file(sha1, type, &size);\n \t\tif (!buffer)\n@@ -213,7 +218,7 @@ static int write_zip_entry(const unsigne\n \t}\n \n \tcopy_le32(dirent.magic, 0x02014b50);\n-\tcopy_le16(dirent.creator_version, 0);\n+\tcopy_le16(dirent.creator_version, S_ISLNK(mode) ? 0x0317 : 0);\n \tcopy_le16(dirent.version, 10);\n \tcopy_le16(dirent.flags, 0);\n \tcopy_le16(dirent.compression_method, method);\n@@ -227,7 +232,7 @@ static int write_zip_entry(const unsigne\n \tcopy_le16(dirent.comment_length, 0);\n \tcopy_le16(dirent.disk, 0);\n \tcopy_le16(dirent.attr1, 0);\n-\tcopy_le32(dirent.attr2, 0);\n+\tcopy_le32(dirent.attr2, attr2);\n \tcopy_le32(dirent.offset, zip_offset);\n \tmemcpy(zip_dir + zip_dir_offset, &dirent, sizeof(struct zip_dir_header));\n \tzip_dir_offset += sizeof(struct zip_dir_header);\ndiff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh\nindex 278eb66..cf08e92 100755\n--- a/t/t5000-tar-tree.sh\n+++ b/t/t5000-tar-tree.sh\n@@ -26,6 +26,7 @@ commit id embedding:\n \n . ./test-lib.sh\n TAR=${TAR:-tar}\n+UNZIP=${UNZIP:-unzip}\n \n test_expect_success \\\n     'populate workdir' \\\n@@ -95,4 +96,38 @@ test_expect_success \\\n     'validate file contents with prefix' \\\n     'diff -r a c/prefix/a'\n \n+test_expect_success \\\n+    'git-archive --format=zip' \\\n+    'git-archive --format=zip HEAD >d.zip'\n+\n+test_expect_success \\\n+    'extract ZIP archive' \\\n+    '(mkdir d && cd d && $UNZIP ../d.zip)'\n+\n+test_expect_success \\\n+    'validate filenames' \\\n+    '(cd d/a && find .) | sort >d.lst &&\n+     diff a.lst d.lst'\n+\n+test_expect_success \\\n+    'validate file contents' \\\n+    'diff -r a d/a'\n+\n+test_expect_success \\\n+    'git-archive --format=zip with prefix' \\\n+    'git-archive --format=zip --prefix=prefix/ HEAD >e.zip'\n+\n+test_expect_success \\\n+    'extract ZIP archive with prefix' \\\n+    '(mkdir e && cd e && $UNZIP ../e.zip)'\n+\n+test_expect_success \\\n+    'validate filenames with prefix' \\\n+    '(cd e/prefix/a && find .) | sort >e.lst &&\n+     diff a.lst e.lst'\n+\n+test_expect_success \\\n+    'validate file contents with prefix' \\\n+    'diff -r a e/prefix/a'\n+\n test_done\n-- \n1.4.2.3.gf59615\n"}]}