diff options
Diffstat (limited to 'src/VBox/Runtime/common/zip/tar.cpp')
-rw-r--r-- | src/VBox/Runtime/common/zip/tar.cpp | 170 |
1 files changed, 113 insertions, 57 deletions
diff --git a/src/VBox/Runtime/common/zip/tar.cpp b/src/VBox/Runtime/common/zip/tar.cpp index cb342d23..e2ced7e6 100644 --- a/src/VBox/Runtime/common/zip/tar.cpp +++ b/src/VBox/Runtime/common/zip/tar.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009-2010 Oracle Corporation + * Copyright (C) 2009-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -40,6 +40,7 @@ #include <iprt/string.h> #include "internal/magics.h" +#include "tar.h" /****************************************************************************** @@ -85,19 +86,10 @@ typedef union RTTARRECORD } RTTARRECORD; AssertCompileSize(RTTARRECORD, 512); AssertCompileMemberOffset(RTTARRECORD, h.size, 100+8*3); +AssertCompileMemberSize(RTTARRECORD, h.name, RTTAR_NAME_MAX+1); /** Pointer to a tar file header. */ typedef RTTARRECORD *PRTTARRECORD; - -#if 0 /* not currently used */ -typedef struct RTTARFILELIST -{ - char *pszFilename; - RTTARFILELIST *pNext; -} RTTARFILELIST; -typedef RTTARFILELIST *PRTTARFILELIST; -#endif - /** Pointer to a tar file handle. */ typedef struct RTTARFILEINTERNAL *PRTTARFILEINTERNAL; @@ -144,10 +136,22 @@ typedef struct RTTARFILEINTERNAL uint64_t offCurrent; /** The open mode. */ uint32_t fOpenMode; + /** The link flag. */ + char linkflag; } RTTARFILEINTERNAL; /** Pointer to the internal data of a tar file. */ typedef RTTARFILEINTERNAL *PRTTARFILEINTERNAL; +#if 0 /* not currently used */ +typedef struct RTTARFILELIST +{ + char *pszFilename; + RTTARFILELIST *pNext; +} RTTARFILELIST; +typedef RTTARFILELIST *PRTTARFILELIST; +#endif + + /****************************************************************************** * Defined Constants And Macros * @@ -265,29 +269,59 @@ DECLINLINE(uint64_t) rtTarRecToSize(PRTTARRECORD pRecord) return (uint64_t)cbSize; } -DECLINLINE(int) rtTarCalcChkSum(PRTTARRECORD pRecord, uint32_t *pChkSum) +/** + * Calculates the TAR header checksums and detects if it's all zeros. + * + * @returns true if all zeros, false if not. + * @param pHdr The header to checksum. + * @param pi32Unsigned Where to store the checksum calculated using + * unsigned chars. This is the one POSIX + * specifies. + * @param pi32Signed Where to store the checksum calculated using + * signed chars. + * + * @remarks The reason why we calculate the checksum as both signed and unsigned + * has to do with various the char C type being signed on some hosts + * and unsigned on others. + * + * @remarks Borrowed from tarvfs.cpp. + */ +static bool rtZipTarCalcChkSum(PCRTZIPTARHDR pHdr, int32_t *pi32Unsigned, int32_t *pi32Signed) { - uint32_t check = 0; - uint32_t zero = 0; - for (size_t i = 0; i < sizeof(RTTARRECORD); ++i) + int32_t i32Unsigned = 0; + int32_t i32Signed = 0; + + /* + * Sum up the entire header. + */ + const char *pch = (const char *)pHdr; + const char *pchEnd = pch + sizeof(*pHdr); + do { - /* Calculate the sum of every byte from the header. The checksum field - * itself is counted as all blanks. */ - if ( i < RT_UOFFSETOF(RTTARRECORD, h.chksum) - || i >= RT_UOFFSETOF(RTTARRECORD, h.linkflag)) - check += pRecord->d[i]; - else - check += ' '; - /* Additional check if all fields are zero, which indicate EOF. */ - zero += pRecord->d[i]; - } + i32Unsigned += *(unsigned char *)pch; + i32Signed += *(signed char *)pch; + } while (++pch != pchEnd); - /* EOF? */ - if (!zero) - return VERR_TAR_END_OF_FILE; + /* + * Check if it's all zeros and replace the chksum field with spaces. + */ + bool const fZeroHdr = i32Unsigned == 0; - *pChkSum = check; - return VINF_SUCCESS; + pch = pHdr->Common.chksum; + pchEnd = pch + sizeof(pHdr->Common.chksum); + do + { + i32Unsigned -= *(unsigned char *)pch; + i32Signed -= *(signed char *)pch; + } while (++pch != pchEnd); + + i32Unsigned += (unsigned char)' ' * sizeof(pHdr->Common.chksum); + i32Signed += (signed char)' ' * sizeof(pHdr->Common.chksum); + + *pi32Unsigned = i32Unsigned; + if (pi32Signed) + *pi32Signed = i32Signed; + return fZeroHdr; } DECLINLINE(int) rtTarReadHeaderRecord(RTFILE hFile, PRTTARRECORD pRecord) @@ -302,16 +336,16 @@ DECLINLINE(int) rtTarReadHeaderRecord(RTFILE hFile, PRTTARRECORD pRecord) return rc; /* Check for data integrity & an EOF record */ - uint32_t check = 0; - rc = rtTarCalcChkSum(pRecord, &check); - /* EOF? */ - if (RT_FAILURE(rc)) - return rc; + int32_t iUnsignedChksum, iSignedChksum; + if (rtZipTarCalcChkSum((PCRTZIPTARHDR)pRecord, &iUnsignedChksum, &iSignedChksum)) + return VERR_TAR_END_OF_FILE; /* Verify the checksum */ uint32_t sum; rc = RTStrToUInt32Full(pRecord->h.chksum, 8, &sum); - if (RT_SUCCESS(rc) && sum == check) + if ( RT_SUCCESS(rc) + && ( sum == (uint32_t)iSignedChksum + || sum == (uint32_t)iUnsignedChksum) ) { /* Make sure the strings are zero terminated. */ pRecord->h.name[sizeof(pRecord->h.name) - 1] = 0; @@ -332,24 +366,27 @@ DECLINLINE(int) rtTarCreateHeaderRecord(PRTTARRECORD pRecord, const char *pszSrc /** @todo check for field overflows. */ /* Fill the header record */ // RT_ZERO(pRecord); - done by the caller. - RTStrPrintf(pRecord->h.name, sizeof(pRecord->h.name), "%s", pszSrcName); - RTStrPrintf(pRecord->h.mode, sizeof(pRecord->h.mode), "%0.7o", fmode); - RTStrPrintf(pRecord->h.uid, sizeof(pRecord->h.uid), "%0.7o", uid); - RTStrPrintf(pRecord->h.gid, sizeof(pRecord->h.gid), "%0.7o", gid); + /** @todo use RTStrCopy */ + size_t cb = RTStrPrintf(pRecord->h.name, sizeof(pRecord->h.name), "%s", pszSrcName); + if (cb < strlen(pszSrcName)) + return VERR_BUFFER_OVERFLOW; + RTStrPrintf(pRecord->h.mode, sizeof(pRecord->h.mode), "%0.7o", fmode); + RTStrPrintf(pRecord->h.uid, sizeof(pRecord->h.uid), "%0.7o", uid); + RTStrPrintf(pRecord->h.gid, sizeof(pRecord->h.gid), "%0.7o", gid); rtTarSizeToRec(pRecord, cbSize); - RTStrPrintf(pRecord->h.mtime, sizeof(pRecord->h.mtime), "%0.11o", mtime); + RTStrPrintf(pRecord->h.mtime, sizeof(pRecord->h.mtime), "%0.11llo", mtime); RTStrPrintf(pRecord->h.magic, sizeof(pRecord->h.magic), "ustar "); RTStrPrintf(pRecord->h.uname, sizeof(pRecord->h.uname), "someone"); RTStrPrintf(pRecord->h.gname, sizeof(pRecord->h.gname), "someone"); pRecord->h.linkflag = LF_NORMAL; /* Create the checksum out of the new header */ - uint32_t uChkSum = 0; - int rc = rtTarCalcChkSum(pRecord, &uChkSum); - if (RT_FAILURE(rc)) - return rc; + int32_t iUnsignedChksum, iSignedChksum; + if (rtZipTarCalcChkSum((PCRTZIPTARHDR)pRecord, &iUnsignedChksum, &iSignedChksum)) + return VERR_TAR_END_OF_FILE; + /* Format the checksum */ - RTStrPrintf(pRecord->h.chksum, sizeof(pRecord->h.chksum), "%0.7o", uChkSum); + RTStrPrintf(pRecord->h.chksum, sizeof(pRecord->h.chksum), "%0.7o", iUnsignedChksum); return VINF_SUCCESS; } @@ -1178,7 +1215,7 @@ RTR3DECL(int) RTTarFileSetTime(RTTARFILE hFile, PRTTIMESPEC pTime) /* Convert the time to an string. */ char szModTime[RT_SIZEOFMEMB(RTTARRECORD, h.mtime)]; - RTStrPrintf(szModTime, sizeof(szModTime), "%0.11o", RTTimeSpecGetSeconds(pTime)); + RTStrPrintf(szModTime, sizeof(szModTime), "%0.11llo", RTTimeSpecGetSeconds(pTime)); /* Write it directly into the header */ return RTFileWriteAt(pFileInt->pTar->hTarFile, @@ -1617,14 +1654,23 @@ RTR3DECL(int) RTTarSeekNextFile(RTTAR hTar) * If not we are somehow busted. */ uint64_t offCur = RTFileTell(pInt->hTarFile); if (!( pInt->pFileCache->offStart <= offCur - && offCur < pInt->pFileCache->offStart + sizeof(RTTARRECORD) + pInt->pFileCache->cbSize)) + && offCur <= pInt->pFileCache->offStart + sizeof(RTTARRECORD) + pInt->pFileCache->cbSize)) return VERR_INVALID_STATE; /* Seek to the next file header. */ uint64_t offNext = RT_ALIGN(pInt->pFileCache->offStart + sizeof(RTTARRECORD) + pInt->pFileCache->cbSize, sizeof(RTTARRECORD)); - rc = RTFileSeek(pInt->hTarFile, offNext - offCur, RTFILE_SEEK_CURRENT, NULL); - if (RT_FAILURE(rc)) - return rc; + if (pInt->pFileCache->cbSize != 0) + { + rc = RTFileSeek(pInt->hTarFile, offNext - offCur, RTFILE_SEEK_CURRENT, NULL); + if (RT_FAILURE(rc)) + return rc; + } + else + { + /* Else delete the last open file cache. Might be recreated below. */ + rtDeleteTarFileInternal(pInt->pFileCache); + pInt->pFileCache = NULL; + } /* Again check the current filename to fill the cache with the new value. */ return RTTarCurrentFile(hTar, NULL); @@ -1648,20 +1694,24 @@ RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **pps /* Is there some cached entry? */ if (pInt->pFileCache) { - /* Are we still direct behind that header? */ - if (pInt->pFileCache->offStart + sizeof(RTTARRECORD) == RTFileTell(pInt->hTarFile)) + if (pInt->pFileCache->offStart + sizeof(RTTARRECORD) < RTFileTell(pInt->hTarFile)) + { + /* Else delete the last open file cache. Might be recreated below. */ + rtDeleteTarFileInternal(pInt->pFileCache); + pInt->pFileCache = NULL; + } + else/* Are we still directly behind that header? */ { /* Yes, so the streaming can start. Just return the cached file * structure to the caller. */ *phFile = rtCopyTarFileInternal(pInt->pFileCache); if (ppszFilename) *ppszFilename = RTStrDup(pInt->pFileCache->pszFilename); + if (pInt->pFileCache->linkflag == LF_DIR) + return VINF_TAR_DIR_PATH; return VINF_SUCCESS; } - /* Else delete the last open file cache. Might be recreated below. */ - rtDeleteTarFileInternal(pInt->pFileCache); - pInt->pFileCache = NULL; } PRTTARFILEINTERNAL pFileInt = NULL; @@ -1679,7 +1729,8 @@ RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **pps /* We support normal files only */ if ( record.h.linkflag == LF_OLDNORMAL - || record.h.linkflag == LF_NORMAL) + || record.h.linkflag == LF_NORMAL + || record.h.linkflag == LF_DIR) { pFileInt = rtCreateTarFileInternal(pInt, record.h.name, fOpen); if (!pFileInt) @@ -1692,11 +1743,16 @@ RTR3DECL(int) RTTarFileOpenCurrentFile(RTTAR hTar, PRTTARFILE phFile, char **pps pFileInt->cbSize = rtTarRecToSize(&record); /* The start is -512 from here. */ pFileInt->offStart = RTFileTell(pInt->hTarFile) - sizeof(RTTARRECORD); + /* remember the type of a file */ + pFileInt->linkflag = record.h.linkflag; /* Copy the new file structure to our cache. */ pInt->pFileCache = rtCopyTarFileInternal(pFileInt); if (ppszFilename) *ppszFilename = RTStrDup(pFileInt->pszFilename); + + if (pFileInt->linkflag == LF_DIR) + rc = VINF_TAR_DIR_PATH; } } while (0); |