Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

463 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #ifndef PACKEDSTORE_H
  7. #define PACKEDSTORE_H
  8. #ifdef _WIN32
  9. #pragma once
  10. #endif
  11. #include <tier0/platform.h>
  12. #include <tier0/threadtools.h>
  13. #include <tier2/tier2.h>
  14. #include "filesystem.h"
  15. #include "tier1/utlintrusivelist.h"
  16. #include "tier1/utlvector.h"
  17. #include "tier1/utlsortvector.h"
  18. #include "tier1/utlmap.h"
  19. //#define VPK_ENABLE_SIGNING
  20. const int k_nVPKDefaultChunkSize = 200 * 1024 * 1024;
  21. class CPackedStore;
  22. struct ChunkHashFraction_t
  23. {
  24. int m_nPackFileNumber;
  25. int m_nFileFraction;
  26. int m_cbChunkLen;
  27. MD5Value_t m_md5contents;
  28. };
  29. class ChunkHashFractionLess_t
  30. {
  31. public:
  32. bool Less( const ChunkHashFraction_t& lhs, const ChunkHashFraction_t& rhs, void *pContext )
  33. {
  34. if ( lhs.m_nPackFileNumber < rhs.m_nPackFileNumber )
  35. return true;
  36. if ( lhs.m_nPackFileNumber > rhs.m_nPackFileNumber )
  37. return false;
  38. if ( lhs.m_nFileFraction < rhs.m_nFileFraction )
  39. return true;
  40. if ( lhs.m_nFileFraction > rhs.m_nFileFraction )
  41. return false;
  42. return false;
  43. }
  44. };
  45. class CPackedStoreFileHandle
  46. {
  47. public:
  48. int m_nFileNumber;
  49. int m_nFileOffset;
  50. int m_nFileSize;
  51. int m_nCurrentFileOffset;
  52. void const *m_pMetaData;
  53. uint16 m_nMetaDataSize;
  54. CPackedStore *m_pOwner;
  55. struct CFileHeaderFixedData *m_pHeaderData;
  56. uint8 *m_pDirFileNamePtr; // pointer to basename in dir block
  57. FORCEINLINE operator bool( void ) const
  58. {
  59. return ( m_nFileNumber != -1 );
  60. }
  61. FORCEINLINE int Read( void *pOutData, int nNumBytes );
  62. CPackedStoreFileHandle( void )
  63. {
  64. m_nFileNumber = -1;
  65. }
  66. int Seek( int nOffset, int nWhence )
  67. {
  68. switch( nWhence )
  69. {
  70. case SEEK_CUR:
  71. nOffset = m_nFileOffset + nOffset ;
  72. break;
  73. case SEEK_END:
  74. nOffset = m_nFileSize + nOffset;
  75. break;
  76. }
  77. m_nCurrentFileOffset = MAX( 0, MIN( m_nFileSize, nOffset ) );
  78. return m_nCurrentFileOffset;
  79. }
  80. int Tell( void ) const
  81. {
  82. return m_nCurrentFileOffset;
  83. }
  84. uint32 GetFileCRCFromHeaderData() const
  85. {
  86. uint32 *pCRC = (uint32 *)m_pHeaderData;
  87. return *pCRC;
  88. }
  89. FORCEINLINE void GetPackFileName( char *pchFileNameOut, int cchFileNameOut );
  90. };
  91. #define MAX_ARCHIVE_FILES_TO_KEEP_OPEN_AT_ONCE 512
  92. #define PACKEDFILE_EXT_HASH_SIZE 15
  93. #ifdef _WIN32
  94. typedef HANDLE PackDataFileHandle_t;
  95. #else
  96. typedef FileHandle_t PackDataFileHandle_t;
  97. #endif
  98. struct FileHandleTracker_t
  99. {
  100. int m_nFileNumber;
  101. PackDataFileHandle_t m_hFileHandle;
  102. int m_nCurOfs;
  103. CThreadFastMutex m_Mutex;
  104. FileHandleTracker_t( void )
  105. {
  106. m_nFileNumber = -1;
  107. }
  108. };
  109. enum ePackedStoreAddResultCode
  110. {
  111. EPADD_NEWFILE, // the file was added and is new
  112. EPADD_ADDSAMEFILE, // the file was already present, and the contents are the same as what you passed.
  113. EPADD_UPDATEFILE, // the file was alreayd present and its contents have been updated
  114. EPADD_ERROR, // some error has resulted
  115. };
  116. // Describe a file inside of a VPK file. Is not memory efficient; only used for interface
  117. // purposes and during file building
  118. struct VPKContentFileInfo_t
  119. {
  120. CUtlString m_sName;
  121. int m_idxChunk;
  122. uint32 m_iTotalSize;
  123. uint32 m_iOffsetInChunk;
  124. uint32 m_iPreloadSize;
  125. const void *m_pPreloadData;
  126. //MD5Value_t m_md5Source; // source content before munging & release optimization. Used for incremental builds
  127. uint32 m_crc; // CRC of actual file contents
  128. /// Size of the data in the chunk file. (Excludes the preload data size)
  129. uint32 GetSizeInChunkFile() const
  130. {
  131. Assert( m_iTotalSize >= m_iPreloadSize );
  132. return m_iTotalSize - m_iPreloadSize;
  133. }
  134. VPKContentFileInfo_t()
  135. {
  136. m_idxChunk = -1;
  137. m_iTotalSize = 0;
  138. m_iOffsetInChunk = 0;
  139. m_iPreloadSize = 0;
  140. m_crc = 0;
  141. m_pPreloadData = NULL;
  142. //memset( m_md5Source.bits, 0, sizeof( m_md5Source.bits ) );
  143. }
  144. };
  145. // a 1MB chunk of cached VPK data
  146. // For CPackedStoreReadCache
  147. struct CachedVPKRead_t
  148. {
  149. CachedVPKRead_t()
  150. {
  151. m_nPackFileNumber = 0;
  152. m_nFileFraction = 0;
  153. m_pubBuffer = NULL;
  154. m_cubBuffer = 0;
  155. m_idxLRU = -1;
  156. m_hMD5RequestHandle= 0;
  157. m_cFailedHashes = 0;
  158. }
  159. int m_nPackFileNumber; // identifier
  160. int m_nFileFraction; // identifier
  161. uint8 *m_pubBuffer; // data
  162. int m_cubBuffer; // data
  163. int m_idxLRU; // bookkeeping
  164. int m_hMD5RequestHandle;// bookkeeping
  165. int m_cFailedHashes; // did the MD5 match what it was supposed to?
  166. MD5Value_t m_md5Value;
  167. MD5Value_t m_md5ValueRetry;
  168. static bool Less( const CachedVPKRead_t& lhs, const CachedVPKRead_t& rhs )
  169. {
  170. if ( lhs.m_nPackFileNumber < rhs.m_nPackFileNumber )
  171. return true;
  172. if ( lhs.m_nPackFileNumber > rhs.m_nPackFileNumber )
  173. return false;
  174. if ( lhs.m_nFileFraction < rhs.m_nFileFraction )
  175. return true;
  176. if ( lhs.m_nFileFraction > rhs.m_nFileFraction )
  177. return false;
  178. return false;
  179. }
  180. };
  181. // Read the VPK file in 1MB chunks
  182. // and we hang on to those chunks so we can serve other reads out of the cache
  183. // This sounds great, but is only of secondary importance.
  184. // The primary reason we do this is so that the FileTracker can calculate the
  185. // MD5 of the 1MB chunks asynchronously in another thread - while we hold
  186. // the chunk in cache - making the MD5 calculation "free"
  187. class CPackedStoreReadCache
  188. {
  189. public:
  190. CPackedStoreReadCache( IBaseFileSystem *pFS );
  191. bool ReadCacheLine( FileHandleTracker_t &fHandle, CachedVPKRead_t &cachedVPKRead, int &nRead );
  192. bool BCanSatisfyFromReadCache( uint8 *pOutData, CPackedStoreFileHandle &handle, FileHandleTracker_t &fHandle, int nDesiredPos, int nNumBytes, int &nRead );
  193. bool BCanSatisfyFromReadCacheInternal( uint8 *pOutData, CPackedStoreFileHandle &handle, FileHandleTracker_t &fHandle, int nDesiredPos, int nNumBytes, int &nRead );
  194. bool CheckMd5Result( CachedVPKRead_t &cachedVPKRead, MD5Value_t &md5Value );
  195. int FindBufferToUse();
  196. void RereadBadCacheLine( CachedVPKRead_t &cachedVPKRead );
  197. void RecheckBadCacheLine( CachedVPKRead_t &cachedVPKRead );
  198. void RetryAllBadCacheLines();
  199. // cache 8 MB total. Caching more wastes too much memory.
  200. // On dedicated servers this cache is not used.
  201. static const int k_nCacheBuffersToKeep = 8;
  202. static const int k_cubCacheBufferSize = 0x00100000; // 1MB
  203. static const int k_nCacheBufferMask = 0x7FF00000;
  204. CThreadRWLock m_rwlock;
  205. CUtlRBTree<CachedVPKRead_t> m_treeCachedVPKRead; // all the reads we have done
  206. CTSQueue<CachedVPKRead_t> m_queueCachedVPKReadsRetry; // all the reads that have failed
  207. CUtlLinkedList<CachedVPKRead_t> m_listCachedVPKReadsFailed; // all the reads that have failed
  208. // current items in the cache
  209. int m_cItemsInCache;
  210. int m_rgCurrentCacheIndex[k_nCacheBuffersToKeep];
  211. CInterlockedUInt m_rgLastUsedTime[k_nCacheBuffersToKeep];
  212. CPackedStore *m_pPackedStore;
  213. IBaseFileSystem *m_pFileSystem;
  214. IThreadedFileMD5Processor *m_pFileTracker;
  215. // stats
  216. int m_cubReadFromCache;
  217. int m_cReadFromCache;
  218. int m_cDiscardsFromCache;
  219. int m_cAddedToCache;
  220. int m_cCacheMiss;
  221. int m_cubCacheMiss;
  222. int m_cFileErrors;
  223. int m_cFileErrorsCorrected;
  224. int m_cFileResultsDifferent;
  225. };
  226. class CPackedStore
  227. {
  228. public:
  229. CPackedStore( char const *pFileBasename, char *pszFName, IBaseFileSystem *pFS, bool bOpenForWrite = false );
  230. void RegisterFileTracker( IThreadedFileMD5Processor *pFileTracker ) { m_pFileTracker = pFileTracker; m_PackedStoreReadCache.m_pFileTracker = pFileTracker; }
  231. CPackedStoreFileHandle OpenFile( char const *pFile );
  232. CPackedStoreFileHandle GetHandleForHashingFiles();
  233. /// Add/update the given file to the directory. Does not write any chunk files
  234. void AddFileToDirectory( const VPKContentFileInfo_t &info );
  235. /// Remove the specified file from the directory. Returns true if removed, false if not found
  236. bool RemoveFileFromDirectory( const char *pszName );
  237. /// Add file, writing file data to the end
  238. /// of the current chunk
  239. ePackedStoreAddResultCode AddFile( char const *pFile, uint16 nMetaDataSize, const void *pFileData, uint32 nFullFileSize, bool bMultiChunk, uint32 const *pCrcToUse = NULL );
  240. // write out the file directory
  241. void Write( void );
  242. int ReadData( CPackedStoreFileHandle &handle, void *pOutData, int nNumBytes );
  243. ~CPackedStore( void );
  244. FORCEINLINE void *DirectoryData( void )
  245. {
  246. return m_DirectoryData.Base();
  247. }
  248. // Get a list of all the files in the zip You are responsible for freeing the contents of
  249. // outFilenames (call outFilenames.PurgeAndDeleteElements).
  250. int GetFileList( CUtlStringList &outFilenames, bool bFormattedOutput, bool bSortedOutput );
  251. // Get a list of all files that match the given wildcard string
  252. int GetFileList( const char *pWildCard, CUtlStringList &outFilenames, bool bFormattedOutput, bool bSortedOutput );
  253. /// Get a list of all files that match the given wildcard string, fetching all the details
  254. /// at once
  255. void GetFileList( const char *pWildcard, CUtlVector<VPKContentFileInfo_t> &outVecResults );
  256. // Get a list of all directories of the given wildcard
  257. int GetFileAndDirLists( const char *pWildCard, CUtlStringList &outDirnames, CUtlStringList &outFilenames, bool bSortedOutput );
  258. int GetFileAndDirLists( CUtlStringList &outDirnames, CUtlStringList &outFilenames, bool bSortedOutput );
  259. bool IsEmpty( void ) const;
  260. /// Hash metadata and chunk files
  261. void HashEverything();
  262. /// Hash all chunk files. Don't forget to rehash the metadata afterwords!
  263. void HashAllChunkFiles();
  264. /// Hash all the metadata. (Everything that's not in the chunk files)
  265. void HashMetadata();
  266. /// Re-hash a single chunk file. Don't forget to rehash the metadata afterwords!
  267. void HashChunkFile( int iChunkFileIndex );
  268. bool HashEntirePackFile( CPackedStoreFileHandle &handle, int64 &nFileSize, int nFileFraction, int nFractionSize, FileHash_t &fileHash );
  269. void ComputeDirectoryHash( MD5Value_t &md5Directory );
  270. void ComputeChunkHash( MD5Value_t &md5ChunkHashes );
  271. MD5Value_t &GetDirFileMD5Value() { return m_TotalFileMD5; }
  272. bool BTestDirectoryHash();
  273. bool BTestMasterChunkHash();
  274. CUtlSortVector<ChunkHashFraction_t, ChunkHashFractionLess_t > &AccessPackFileHashes() { return m_vecChunkHashFraction; }
  275. bool FindFileHashFraction( int nPackFileNumber, int nFileFraction, ChunkHashFraction_t &chunkFileHashFraction );
  276. void GetPackFileLoadErrorSummary( CUtlString &sErrors );
  277. void GetPackFileLoadErrorSummaryKV( KeyValues *pKV );
  278. void GetPackFileName( CPackedStoreFileHandle &handle, char *pchFileNameOut, int cchFileNameOut ) const;
  279. void GetDataFileName( char *pchFileNameOut, int cchFileNameOut, int nFileNumber ) const;
  280. char const *BaseName( void )
  281. {
  282. return m_pszFileBaseName;
  283. }
  284. char const *FullPathName( void )
  285. {
  286. return m_pszFullPathName;
  287. }
  288. void SetWriteChunkSize( int nWriteChunkSize )
  289. {
  290. m_nWriteChunkSize = nWriteChunkSize;
  291. }
  292. int GetWriteChunkSize() const { return m_nWriteChunkSize; }
  293. int GetHighestChunkFileIndex() { return m_nHighestChunkFileIndex; }
  294. void DiscardChunkHashes( int iChunkFileIndex );
  295. const CUtlVector<uint8> &GetSignaturePublicKey() const { return m_SignaturePublicKey; }
  296. const CUtlVector<uint8> &GetSignature() const { return m_Signature; }
  297. #ifdef VPK_ENABLE_SIGNING
  298. enum ESignatureCheckResult
  299. {
  300. eSignatureCheckResult_NotSigned,
  301. eSignatureCheckResult_WrongKey,
  302. eSignatureCheckResult_Failed, // IO error, etc
  303. eSignatureCheckResult_InvalidSignature,
  304. eSignatureCheckResult_ValidSignature,
  305. };
  306. ESignatureCheckResult CheckSignature( int nSignatureSize, const void *pSignature ) const;
  307. void SetKeysForSigning( int nPrivateKeySize, const void *pPrivateKeyData, int nPublicKeySize, const void *pPublicKeyData );
  308. #endif
  309. void SetUseDirFile() { m_bUseDirFile = true; }
  310. int m_PackFileID;
  311. private:
  312. char m_pszFileBaseName[MAX_PATH];
  313. char m_pszFullPathName[MAX_PATH];
  314. int m_nDirectoryDataSize;
  315. int m_nWriteChunkSize;
  316. bool m_bUseDirFile;
  317. IBaseFileSystem *m_pFileSystem;
  318. IThreadedFileMD5Processor *m_pFileTracker;
  319. CThreadFastMutex m_Mutex;
  320. CPackedStoreReadCache m_PackedStoreReadCache;
  321. CUtlIntrusiveList<class CFileExtensionData> m_pExtensionData[PACKEDFILE_EXT_HASH_SIZE];
  322. CUtlVector<uint8> m_DirectoryData;
  323. CUtlBlockVector<uint8> m_EmbeddedChunkData;
  324. CUtlSortVector<ChunkHashFraction_t, ChunkHashFractionLess_t > m_vecChunkHashFraction;
  325. bool BFileContainedHashes() { return m_vecChunkHashFraction.Count() > 0; }
  326. // these are valid if BFileContainedHashes() is true
  327. MD5Value_t m_DirectoryMD5;
  328. MD5Value_t m_ChunkHashesMD5;
  329. MD5Value_t m_TotalFileMD5;
  330. int m_nHighestChunkFileIndex;
  331. /// The private key that will be used to sign the directory file.
  332. /// This will be empty for unsigned VPK's, or if we don't know the
  333. /// private key.
  334. CUtlVector<uint8> m_SignaturePrivateKey;
  335. /// The public key in the VPK.
  336. CUtlVector<uint8> m_SignaturePublicKey;
  337. /// The signature that was read / computed
  338. CUtlVector<uint8> m_Signature;
  339. /// The number of bytes in the dir file that were signed
  340. uint32 m_nSizeOfSignedData;
  341. FileHandleTracker_t m_FileHandles[MAX_ARCHIVE_FILES_TO_KEEP_OPEN_AT_ONCE];
  342. void Init( void );
  343. struct CFileHeaderFixedData *FindFileEntry(
  344. char const *pDirname, char const *pBaseName, char const *pExtension,
  345. uint8 **pExtBaseOut = NULL, uint8 **pNameBaseOut = NULL );
  346. void BuildHashTables( void );
  347. FileHandleTracker_t &GetFileHandle( int nFileNumber );
  348. void CloseWriteHandle( void );
  349. // For cache-ing directory and contents data
  350. CUtlStringList m_directoryList; // The index of this list of directories...
  351. CUtlMap<int, CUtlStringList*> m_dirContents; // ...is the key to this map of filenames
  352. void BuildFindFirstCache();
  353. bool InternalRemoveFileFromDirectory( const char *pszName );
  354. friend class CPackedStoreReadCache;
  355. };
  356. FORCEINLINE int CPackedStoreFileHandle::Read( void *pOutData, int nNumBytes )
  357. {
  358. return m_pOwner->ReadData( *this, pOutData, nNumBytes );
  359. }
  360. FORCEINLINE void CPackedStoreFileHandle::GetPackFileName( char *pchFileNameOut, int cchFileNameOut )
  361. {
  362. m_pOwner->GetPackFileName( *this, pchFileNameOut, cchFileNameOut );
  363. }
  364. #endif // packedtsore_h