From f2f465960bbf0608707dbc3d028c1ce3167c3f6c Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 4 Jul 2018 23:47:03 +0100 Subject: [PATCH] Tar code: handle symlink and PAX extensions This now accepts the GitLab .tgz archives without problems, eg the Tu-144. --- simgear/io/badTar.tgz | Bin 0 -> 637 bytes simgear/io/test_untar.cxx | 61 ++++++++++++++++++++++++- simgear/io/untar.cxx | 93 ++++++++++++++++++++++++++++++++++---- 3 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 simgear/io/badTar.tgz diff --git a/simgear/io/badTar.tgz b/simgear/io/badTar.tgz new file mode 100644 index 0000000000000000000000000000000000000000..ecdd73a31ba7a3442e035f88c04b6dc357ba332e GIT binary patch literal 637 zcmV-@0)qV?iwFRbO+8xx1MS+~ZksR|24Hv6v~?G-V;}5jQmHW*q@87pv`y;Iv}M}P zDkpJLRtF+*r1oxl*z4I9%}Ll6h5VsfL{;8LL8J)*!^aB-YGnQ+G1u>9|3j7XTwniy^S|{Mekg4H<2Vq6G8Zej&4JgS zJb(RyJR{^>_UPp|`jeKXpOIrba68=FVfy#)-DcV9biS5vr@wfx!`r1vj{blCrk9O8 zIUd`6ZKRCsOC#T1-JG{S8#Ve~sJtVG1tHzBP71@={mXI&M+EE7z z#>8^|g&!gRP57j=&i361*qc@7|E|ovzSho`QS1C4#Ww#Divr}o5jg)Jv}*ocr+pO4Xt%9ah^IzPJx|vRTCpt-QRNot?!$&To z#{C}=`T=D;V8SPq7rk7mw7AZUgI26@;Q3$G|8u?B>-7J){lZ@Vg)xr*%|QR}pshIm zJ3r`d@Bgvo{Bwc(zm1r8{k~j$EW3XDZVxo8n*TwTdFoat9T|?Idrx*bnN| zKd^m4+yC 0) { @@ -197,13 +212,7 @@ public: currentFile->close(); currentFile.reset(); } - size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE; - if (pad) { - bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad; - setState(READING_PADDING); - } else { - setState(READING_HEADER); - } + readPaddingIfRequired(); } else if (state == READING_HEADER) { processHeader(); } else if (state == PRE_END_OF_ARCHVE) { @@ -212,6 +221,12 @@ public: } else { // what does the spec say here? } + } else if (state == READING_PAX_GLOBAL_ATTRIBUTES) { + parsePAXAttributes(true); + readPaddingIfRequired(); + } else if (state == READING_PAX_FILE_ATTRIBUTES) { + parsePAXAttributes(false); + readPaddingIfRequired(); } else if (state == READING_PADDING) { setState(READING_HEADER); } @@ -317,7 +332,11 @@ public: skipCurrentEntry = false; std::string tarPath = std::string(header.prefix) + std::string(header.fileName); - + if (!paxPathName.empty()) { + tarPath = paxPathName; + paxPathName.clear(); // clear for next file + } + if (!isSafePath(tarPath)) { SG_LOG(SG_IO, SG_WARN, "bad tar path:" << tarPath); skipCurrentEntry = true; @@ -346,6 +365,20 @@ public: currentFile->open(SG_IO_OUT); } setState(READING_FILE); + } else if (header.typeflag == PAX_GLOBAL_HEADER) { + setState(READING_PAX_GLOBAL_ATTRIBUTES); + currentFileSize = ::strtol(header.size, NULL, 8); + bytesRemaining = currentFileSize; + paxAttributes.clear(); + } else if (header.typeflag == PAX_FILE_ATTRIBUTES) { + setState(READING_PAX_FILE_ATTRIBUTES); + currentFileSize = ::strtol(header.size, NULL, 8); + bytesRemaining = currentFileSize; + paxAttributes.clear(); + } else if ((header.typeflag == SYMTYPE) || (header.typeflag == LNKTYPE)) { + SG_LOG(SG_IO, SG_WARN, "Tarball contains a link or symlink, will be skipped:" << tarPath); + skipCurrentEntry = true; + setState(READING_HEADER); } else { SG_LOG(SG_IO, SG_WARN, "Unsupported tar file type:" << header.typeflag); state = BAD_ARCHIVE; @@ -370,6 +403,9 @@ public: headerPtr += curBytes; } else if (state == READING_PADDING) { bytesRemaining -= curBytes; + } else if ((state == READING_PAX_FILE_ATTRIBUTES) || (state == READING_PAX_GLOBAL_ATTRIBUTES)) { + bytesRemaining -= curBytes; + paxAttributes.append(bytes, curBytes); } checkEndOfState(); @@ -390,6 +426,39 @@ public: return true; } + + // https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.bpxa500/paxex.htm#paxex + void parsePAXAttributes(bool areGlobal) + { + auto lineStart = 0; + for (;;) { + auto firstSpace = paxAttributes.find(' ', lineStart); + auto firstEq = paxAttributes.find('=', lineStart); + if ((firstEq == std::string::npos) || (firstSpace == std::string::npos)) { + SG_LOG(SG_IO, SG_WARN, "Malfroemd PAX attributes in tarfile"); + break; + } + + uint32_t lengthBytes = std::stoul(paxAttributes.substr(lineStart, firstSpace)); + uint32_t dataBytes = lengthBytes - (firstEq + 1); + std::string name = paxAttributes.substr(firstSpace+1, firstEq - (firstSpace + 1)); + + // dataBytes - 1 here to trim off the trailing newline + std::string data = paxAttributes.substr(firstEq+1, dataBytes - 1); + + processPAXAttribute(areGlobal, name, data); + + lineStart += lengthBytes; + } + } + + void processPAXAttribute(bool isGlobalAttr, const std::string& attrName, const std::string& data) + { + if (!isGlobalAttr && (attrName == "path")) { + // data is UTF-8 encoded path name + paxPathName = data; + } + } }; /////////////////////////////////////////////////////////////////////////////// @@ -428,7 +497,11 @@ public: fill_memory_filefunc(&memoryAccessFuncs); char bufferName[128]; - ::snprintf(bufferName, 128, "%p+%llx", m_buffer.data(), m_buffer.size()); +#if defined(SG_WINDOWS) + ::snprintf(bufferName, 128, "%p+%llx", m_buffer.data(), m_buffer.size()); +#else + ::snprintf(bufferName, 128, "%p+%lx", m_buffer.data(), m_buffer.size()); +#endif unzFile zip = unzOpen2(bufferName, &memoryAccessFuncs); const size_t BUFFER_SIZE = 32 * 1024;