📄 urlcache.c
字号:
/*
* Wininet - Url Cache functions
*
* Copyright 2001,2002 CodeWeavers
* Copyright 2003 Robert Shearman
*
* Eric Kohl
* Aric Stewart
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define COM_NO_WINDOWS_H
#include "config.h"
#include "wine/port.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "wininet.h"
#include "winerror.h"
#include "internet.h"
#include "winreg.h"
#include "shlwapi.h"
#include "wingdi.h"
#include "shlobj.h"
#include "wine/unicode.h"
#include "wine/list.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(wininet);
#define ENTRY_START_OFFSET 0x4000
#define DIR_LENGTH 8
#define BLOCKSIZE 128
#define HASHTABLE_SIZE 448
#define HASHTABLE_BLOCKSIZE 7
#define HASHTABLE_FREE 3
#define ALLOCATION_TABLE_OFFSET 0x250
#define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
#define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
#define NEWFILE_NUM_BLOCKS 0xd80
#define NEWFILE_SIZE (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
#define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
#define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
#define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
#define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
#define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
#define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
typedef struct _CACHEFILE_ENTRY
{
/* union
{*/
DWORD dwSignature; /* e.g. "URL " */
/* CHAR szSignature[4];
};*/
DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
} CACHEFILE_ENTRY;
typedef struct _URL_CACHEFILE_ENTRY
{
CACHEFILE_ENTRY CacheFileEntry;
FILETIME LastModifiedTime;
FILETIME LastAccessTime;
WORD wExpiredDate; /* expire date in dos format */
WORD wExpiredTime; /* expire time in dos format */
DWORD dwUnknown1; /* usually zero */
DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
DWORD dwUnknown2; /* usually zero */
DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
DWORD dwUnknown3; /* usually 0x60 */
DWORD dwOffsetUrl; /* usually 0x68 */
BYTE CacheDir; /* index of cache directory this url is stored in */
BYTE Unknown4; /* usually zero */
WORD wUnknown5; /* usually 0x1010 */
DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
DWORD dwHeaderInfoSize;
DWORD dwUnknown6; /* usually zero */
WORD wLastSyncDate; /* last sync date in dos format */
WORD wLastSyncTime; /* last sync time in dos format */
DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
WORD wUnknownDate; /* usually same as wLastSyncDate */
WORD wUnknownTime; /* usually same as wLastSyncTime */
DWORD dwUnknown7; /* usually zero */
DWORD dwUnknown8; /* usually zero */
CHAR szSourceUrlName[1]; /* start of url */
/* packing to dword align start of next field */
/* CHAR szLocalFileName[]; (local file name exluding path) */
/* packing to dword align start of next field */
/* CHAR szHeaderInfo[]; (header info) */
} URL_CACHEFILE_ENTRY;
struct _HASH_ENTRY
{
DWORD dwHashKey;
DWORD dwOffsetEntry;
};
typedef struct _HASH_CACHEFILE_ENTRY
{
CACHEFILE_ENTRY CacheFileEntry;
DWORD dwAddressNext;
DWORD dwHashTableNumber;
struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
} HASH_CACHEFILE_ENTRY;
typedef struct _DIRECTORY_DATA
{
DWORD dwUnknown;
char filename[DIR_LENGTH];
} DIRECTORY_DATA;
typedef struct _URLCACHE_HEADER
{
char szSignature[28];
DWORD dwFileSize;
DWORD dwOffsetFirstHashTable;
DWORD dwIndexCapacityInBlocks;
DWORD dwBlocksInUse;
DWORD dwUnknown1;
DWORD dwCacheLimitLow; /* disk space limit for cache */
DWORD dwCacheLimitHigh; /* disk space limit for cache */
DWORD dwUnknown4; /* current disk space usage for cache */
DWORD dwUnknown5; /* current disk space usage for cache */
DWORD dwUnknown6; /* possibly a flag? */
DWORD dwUnknown7;
BYTE DirectoryCount; /* number of directory_data's */
BYTE Unknown8[3]; /* just padding? */
DIRECTORY_DATA directory_data[1]; /* first directory entry */
} URLCACHE_HEADER, *LPURLCACHE_HEADER;
typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
typedef struct _STREAM_HANDLE
{
HANDLE hFile;
CHAR lpszUrl[1];
} STREAM_HANDLE;
typedef struct _URLCACHECONTAINER
{
struct list entry; /* part of a list */
LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
LPWSTR path; /* path to url container directory */
HANDLE hMapping; /* handle of file mapping */
DWORD file_size; /* size of file when mapping was opened */
HANDLE hMutex; /* hande of mutex */
} URLCACHECONTAINER;
/* List of all containers available */
static struct list UrlContainers = LIST_INIT(UrlContainers);
static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash);
/***********************************************************************
* URLCache_PathToObjectName (Internal)
*
* Converts a path to a name suitable for use as a Win32 object name.
* Replaces '\\' characters in-place with the specified character
* (usually '_' or '!')
*
* RETURNS
* nothing
*
*/
static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
{
for (; *lpszPath; lpszPath++)
{
if (*lpszPath == '\\')
*lpszPath = replace;
}
}
/***********************************************************************
* URLCacheContainer_OpenIndex (Internal)
*
* Opens the index file and saves mapping handle in hCacheIndexMapping
*
* RETURNS
* TRUE if succeeded
* FALSE if failed
*
*/
static BOOL URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
{
HANDLE hFile;
WCHAR wszFilePath[MAX_PATH];
DWORD dwFileSize;
static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
if (pContainer->hMapping)
return TRUE;
strcpyW(wszFilePath, pContainer->path);
strcatW(wszFilePath, wszIndex);
hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
/* Maybe the directory wasn't there? Try to create it */
if (CreateDirectoryW(pContainer->path, 0))
hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
}
if (hFile == INVALID_HANDLE_VALUE)
{
TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
return FALSE;
}
/* At this stage we need the mutex because we may be about to create the
* file.
*/
WaitForSingleObject(pContainer->hMutex, INFINITE);
dwFileSize = GetFileSize(hFile, NULL);
if (dwFileSize == INVALID_FILE_SIZE)
{
ReleaseMutex(pContainer->hMutex);
return FALSE;
}
if (dwFileSize == 0)
{
static CHAR const szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Cache\\Content";
HKEY key;
char achZeroes[0x1000];
DWORD dwOffset;
DWORD dwError = 0;
/* Write zeroes to the entire file so we can safely map it without
* fear of getting a SEGV because the disk is full.
*/
memset(achZeroes, 0, sizeof(achZeroes));
for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
{
DWORD dwWrite = sizeof(achZeroes);
DWORD dwWritten;
if (NEWFILE_SIZE - dwOffset < dwWrite)
dwWrite = NEWFILE_SIZE - dwOffset;
if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
dwWritten != dwWrite)
{
/* If we fail to write, we need to return the error that
* cause the problem and also make sure the file is no
* longer there, if possible.
*/
dwError = GetLastError();
break;
}
}
if (!dwError)
{
HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
if (hMapping)
{
URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
if (pHeader)
{
WCHAR *pwchDir;
WCHAR wszDirPath[MAX_PATH];
FILETIME ft;
int i, j;
dwFileSize = NEWFILE_SIZE;
/* First set some constants and defaults in the header */
strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
pHeader->dwFileSize = dwFileSize;
pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
/* 127MB - taken from default for Windows 2000 */
pHeader->dwCacheLimitHigh = 0;
pHeader->dwCacheLimitLow = 0x07ff5400;
/* Copied from a Windows 2000 cache index */
pHeader->DirectoryCount = 4;
/* If the registry has a cache size set, use the registry value */
if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
{
DWORD dw;
DWORD len = sizeof(dw);
DWORD keytype;
if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
(BYTE *) &dw, &len) == ERROR_SUCCESS &&
keytype == REG_DWORD)
{
pHeader->dwCacheLimitHigh = (dw >> 22);
pHeader->dwCacheLimitLow = dw << 10;
}
RegCloseKey(key);
}
URLCache_CreateHashTable(pHeader, NULL);
/* Last step - create the directories */
strcpyW(wszDirPath, pContainer->path);
pwchDir = wszDirPath + strlenW(wszDirPath);
pwchDir[8] = 0;
GetSystemTimeAsFileTime(&ft);
for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
{
/* The following values were copied from a Windows index.
* I don't know what the values are supposed to mean but
* have made them the same in the hope that this will
* be better for compatibility
*/
pHeader->directory_data[i].dwUnknown = (i > 1) ? 0xfe : 0xff;
for (j = 0;; ++j)
{
int k;
ULONGLONG n = ft.dwHighDateTime;
/* Generate a file name to attempt to create.
* This algorithm will create what will appear
* to be random and unrelated directory names
* of up to 9 characters in length.
*/
n <<= 32;
n += ft.dwLowDateTime;
n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
for (k = 0; k < 8; ++k)
{
int r = (n % 36);
/* Dividing by a prime greater than 36 helps
* with the appearance of randomness
*/
n /= 37;
if (r < 10)
pwchDir[k] = '0' + r;
else
pwchDir[k] = 'A' + (r - 10);
}
if (CreateDirectoryW(wszDirPath, 0))
{
int k;
/* The following is OK because we generated an
* 8 character directory name made from characters
* [A-Z0-9], which are equivalent for all code
* pages and for UTF-16
*/
for (k = 0; k < 8; ++k)
pHeader->directory_data[i].filename[k] = pwchDir[k];
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -