2003-01-10 18:33:35 +00:00
|
|
|
/*
|
2004-04-27 12:31:57 +00:00
|
|
|
* winmisc.c: miscellaneous Windows-specific things
|
2003-01-10 18:33:35 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "putty.h"
|
2015-08-11 09:25:40 +00:00
|
|
|
#ifndef SECURITY_WIN32
|
2014-11-01 14:44:16 +00:00
|
|
|
#define SECURITY_WIN32
|
2015-08-11 09:25:40 +00:00
|
|
|
#endif
|
2010-03-24 20:12:25 +00:00
|
|
|
#include <security.h>
|
2003-01-10 18:33:35 +00:00
|
|
|
|
2003-08-21 19:48:45 +00:00
|
|
|
OSVERSIONINFO osVersion;
|
|
|
|
|
2004-10-06 22:31:07 +00:00
|
|
|
char *platform_get_x_display(void) {
|
|
|
|
/* We may as well check for DISPLAY in case it's useful. */
|
|
|
|
return dupstr(getenv("DISPLAY"));
|
|
|
|
}
|
|
|
|
|
2011-10-02 11:01:57 +00:00
|
|
|
Filename *filename_from_str(const char *str)
|
2003-02-01 12:54:40 +00:00
|
|
|
{
|
2011-10-02 11:01:57 +00:00
|
|
|
Filename *ret = snew(Filename);
|
|
|
|
ret->path = dupstr(str);
|
2003-02-01 12:54:40 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-10-02 11:01:57 +00:00
|
|
|
Filename *filename_copy(const Filename *fn)
|
|
|
|
{
|
|
|
|
return filename_from_str(fn->path);
|
|
|
|
}
|
|
|
|
|
2003-02-01 17:24:27 +00:00
|
|
|
const char *filename_to_str(const Filename *fn)
|
2003-02-01 12:54:40 +00:00
|
|
|
{
|
2003-02-01 17:24:27 +00:00
|
|
|
return fn->path;
|
2003-02-01 12:54:40 +00:00
|
|
|
}
|
|
|
|
|
2011-10-02 11:01:57 +00:00
|
|
|
int filename_equal(const Filename *f1, const Filename *f2)
|
|
|
|
{
|
|
|
|
return !strcmp(f1->path, f2->path);
|
|
|
|
}
|
|
|
|
|
|
|
|
int filename_is_null(const Filename *fn)
|
2003-02-01 12:54:40 +00:00
|
|
|
{
|
2011-10-02 11:01:57 +00:00
|
|
|
return !*fn->path;
|
2003-02-01 12:54:40 +00:00
|
|
|
}
|
|
|
|
|
2011-10-02 11:01:57 +00:00
|
|
|
void filename_free(Filename *fn)
|
|
|
|
{
|
|
|
|
sfree(fn->path);
|
|
|
|
sfree(fn);
|
|
|
|
}
|
|
|
|
|
2018-05-24 09:48:20 +00:00
|
|
|
void filename_serialise(BinarySink *bs, const Filename *f)
|
2011-10-02 11:01:57 +00:00
|
|
|
{
|
2018-05-24 09:48:20 +00:00
|
|
|
put_asciz(bs, f->path);
|
2011-10-02 11:01:57 +00:00
|
|
|
}
|
|
|
|
Filename *filename_deserialise(void *vdata, int maxsize, int *used)
|
2003-02-01 12:54:40 +00:00
|
|
|
{
|
2011-10-02 11:01:57 +00:00
|
|
|
char *data = (char *)vdata;
|
|
|
|
char *end;
|
|
|
|
end = memchr(data, '\0', maxsize);
|
|
|
|
if (!end)
|
|
|
|
return NULL;
|
|
|
|
end++;
|
|
|
|
*used = end - data;
|
|
|
|
return filename_from_str(data);
|
2003-02-01 12:54:40 +00:00
|
|
|
}
|
2003-03-06 13:24:02 +00:00
|
|
|
|
2015-09-25 08:23:26 +00:00
|
|
|
char filename_char_sanitise(char c)
|
|
|
|
{
|
|
|
|
if (strchr("<>:\"/\\|?*", c))
|
|
|
|
return '.';
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2012-09-18 23:05:29 +00:00
|
|
|
#ifndef NO_SECUREZEROMEMORY
|
2012-07-22 19:51:50 +00:00
|
|
|
/*
|
|
|
|
* Windows implementation of smemclr (see misc.c) using SecureZeroMemory.
|
|
|
|
*/
|
|
|
|
void smemclr(void *b, size_t n) {
|
|
|
|
if (b && n > 0)
|
|
|
|
SecureZeroMemory(b, n);
|
|
|
|
}
|
2012-09-13 22:34:18 +00:00
|
|
|
#endif
|
2012-07-22 19:51:50 +00:00
|
|
|
|
2003-08-25 13:53:41 +00:00
|
|
|
char *get_username(void)
|
|
|
|
{
|
|
|
|
DWORD namelen;
|
|
|
|
char *user;
|
2010-03-24 20:12:25 +00:00
|
|
|
int got_username = FALSE;
|
|
|
|
DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA,
|
|
|
|
(EXTENDED_NAME_FORMAT, LPSTR, PULONG));
|
2003-08-25 13:53:41 +00:00
|
|
|
|
2010-03-24 20:12:25 +00:00
|
|
|
{
|
|
|
|
static int tried_usernameex = FALSE;
|
|
|
|
if (!tried_usernameex) {
|
|
|
|
/* Not available on Win9x, so load dynamically */
|
2010-09-13 08:29:45 +00:00
|
|
|
HMODULE secur32 = load_system32_dll("secur32.dll");
|
2017-04-11 12:04:00 +00:00
|
|
|
/* If MIT Kerberos is installed, the following call to
|
|
|
|
GET_WINDOWS_FUNCTION makes Windows implicitly load
|
|
|
|
sspicli.dll WITHOUT proper path sanitizing, so better
|
|
|
|
load it properly before */
|
|
|
|
HMODULE sspicli = load_system32_dll("sspicli.dll");
|
2017-12-10 09:19:15 +00:00
|
|
|
(void)sspicli; /* squash compiler warning about unused variable */
|
2010-03-24 20:12:25 +00:00
|
|
|
GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);
|
|
|
|
tried_usernameex = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p_GetUserNameExA) {
|
2004-12-30 16:45:11 +00:00
|
|
|
/*
|
2010-03-24 20:12:25 +00:00
|
|
|
* If available, use the principal -- this avoids the problem
|
|
|
|
* that the local username is case-insensitive but Kerberos
|
|
|
|
* usernames are case-sensitive.
|
2004-12-30 16:45:11 +00:00
|
|
|
*/
|
2010-03-24 20:12:25 +00:00
|
|
|
|
|
|
|
/* Get the length */
|
|
|
|
namelen = 0;
|
|
|
|
(void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen);
|
|
|
|
|
|
|
|
user = snewn(namelen, char);
|
|
|
|
got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen);
|
|
|
|
if (got_username) {
|
|
|
|
char *p = strchr(user, '@');
|
|
|
|
if (p) *p = 0;
|
|
|
|
} else {
|
|
|
|
sfree(user);
|
|
|
|
}
|
2004-12-30 16:45:11 +00:00
|
|
|
}
|
2003-08-25 13:53:41 +00:00
|
|
|
|
2010-03-24 20:12:25 +00:00
|
|
|
if (!got_username) {
|
|
|
|
/* Fall back to local user name */
|
|
|
|
namelen = 0;
|
|
|
|
if (GetUserName(NULL, &namelen) == FALSE) {
|
|
|
|
/*
|
|
|
|
* Apparently this doesn't work at least on Windows XP SP2.
|
|
|
|
* Thus assume a maximum of 256. It will fail again if it
|
|
|
|
* doesn't fit.
|
|
|
|
*/
|
|
|
|
namelen = 256;
|
|
|
|
}
|
|
|
|
|
|
|
|
user = snewn(namelen, char);
|
|
|
|
got_username = GetUserName(user, &namelen);
|
|
|
|
if (!got_username) {
|
|
|
|
sfree(user);
|
|
|
|
}
|
|
|
|
}
|
2003-08-25 13:53:41 +00:00
|
|
|
|
2010-03-24 20:12:25 +00:00
|
|
|
return got_username ? user : NULL;
|
2003-08-25 13:53:41 +00:00
|
|
|
}
|
|
|
|
|
2016-07-18 19:02:32 +00:00
|
|
|
void dll_hijacking_protection(void)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If the OS provides it, call SetDefaultDllDirectories() to
|
|
|
|
* prevent DLLs from being loaded from the directory containing
|
|
|
|
* our own binary, and instead only load from system32.
|
|
|
|
*
|
|
|
|
* This is a protection against hijacking attacks, if someone runs
|
|
|
|
* PuTTY directly from their web browser's download directory
|
|
|
|
* having previously been enticed into clicking on an unwise link
|
|
|
|
* that downloaded a malicious DLL to the same directory under one
|
|
|
|
* of various magic names that seem to be things that standard
|
|
|
|
* Windows DLLs delegate to.
|
|
|
|
*
|
|
|
|
* It shouldn't break deliberate loading of user-provided DLLs
|
|
|
|
* such as GSSAPI providers, because those are specified by their
|
|
|
|
* full pathname by the user-provided configuration.
|
|
|
|
*/
|
|
|
|
static HMODULE kernel32_module;
|
|
|
|
DECL_WINDOWS_FUNCTION(static, BOOL, SetDefaultDllDirectories, (DWORD));
|
|
|
|
|
|
|
|
if (!kernel32_module) {
|
|
|
|
kernel32_module = load_system32_dll("kernel32.dll");
|
2017-06-20 18:02:48 +00:00
|
|
|
#if (defined _MSC_VER && _MSC_VER < 1900) || defined COVERITY
|
|
|
|
/* For older Visual Studio, and also for the system I
|
|
|
|
* currently use for Coveritying the Windows code, this
|
|
|
|
* function isn't available in the header files to
|
|
|
|
* type-check */
|
Add automatic type-checking to GET_WINDOWS_FUNCTION.
This gives me an extra safety-check against having mistyped one of the
function prototypes that we load at run time from DLLs: we verify that
the typedef we defined based on the prototype in our source code
matches the type of the real function as declared in the Windows
headers.
This was an idea I had while adding a pile of further functions using
this mechanism. It didn't catch any errors (either in the new
functions or in the existing collection), but that's no reason not to
keep it anyway now that I've thought of it!
In VS2015, this automated type-check works for most functions, but a
couple manage to break it. SetCurrentProcessExplicitAppUserModelID in
winjump.c can't be type-checked, because including <shobjidl.h> where
that function is declared would also bring in a load of other stuff
that conflicts with the painful manual COM declarations in winjump.c.
(That stuff could probably be removed now we're on an up-to-date
Visual Studio, on the other hand, but that's a separate chore.) And
gai_strerror, used in winnet.c, does _have_ an implementation in a
DLL, but the header files like to provide an inline version with a
different calling convention, which defeats this error-checking trick.
And in the older VS2003 that we still precautionarily build with,
several more type-checks have to be #ifdeffed out because the
functions they check against just aren't there at all.
2017-04-11 17:56:55 +00:00
|
|
|
GET_WINDOWS_FUNCTION_NO_TYPECHECK(
|
|
|
|
kernel32_module, SetDefaultDllDirectories);
|
|
|
|
#else
|
2016-07-18 19:02:32 +00:00
|
|
|
GET_WINDOWS_FUNCTION(kernel32_module, SetDefaultDllDirectories);
|
Add automatic type-checking to GET_WINDOWS_FUNCTION.
This gives me an extra safety-check against having mistyped one of the
function prototypes that we load at run time from DLLs: we verify that
the typedef we defined based on the prototype in our source code
matches the type of the real function as declared in the Windows
headers.
This was an idea I had while adding a pile of further functions using
this mechanism. It didn't catch any errors (either in the new
functions or in the existing collection), but that's no reason not to
keep it anyway now that I've thought of it!
In VS2015, this automated type-check works for most functions, but a
couple manage to break it. SetCurrentProcessExplicitAppUserModelID in
winjump.c can't be type-checked, because including <shobjidl.h> where
that function is declared would also bring in a load of other stuff
that conflicts with the painful manual COM declarations in winjump.c.
(That stuff could probably be removed now we're on an up-to-date
Visual Studio, on the other hand, but that's a separate chore.) And
gai_strerror, used in winnet.c, does _have_ an implementation in a
DLL, but the header files like to provide an inline version with a
different calling convention, which defeats this error-checking trick.
And in the older VS2003 that we still precautionarily build with,
several more type-checks have to be #ifdeffed out because the
functions they check against just aren't there at all.
2017-04-11 17:56:55 +00:00
|
|
|
#endif
|
2016-07-18 19:02:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (p_SetDefaultDllDirectories) {
|
2017-04-03 19:30:18 +00:00
|
|
|
/* LOAD_LIBRARY_SEARCH_SYSTEM32 and explicitly specified
|
|
|
|
* directories only */
|
|
|
|
p_SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 |
|
|
|
|
LOAD_LIBRARY_SEARCH_USER_DIRS);
|
2016-07-18 19:02:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-08-21 19:48:45 +00:00
|
|
|
BOOL init_winver(void)
|
|
|
|
{
|
|
|
|
ZeroMemory(&osVersion, sizeof(osVersion));
|
|
|
|
osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
|
|
|
|
return GetVersionEx ( (OSVERSIONINFO *) &osVersion);
|
|
|
|
}
|
|
|
|
|
2010-09-13 08:29:45 +00:00
|
|
|
HMODULE load_system32_dll(const char *libname)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Wrapper function to load a DLL out of c:\windows\system32
|
|
|
|
* without going through the full DLL search path. (Hence no
|
|
|
|
* attack is possible by placing a substitute DLL earlier on that
|
|
|
|
* path.)
|
|
|
|
*/
|
|
|
|
static char *sysdir = NULL;
|
|
|
|
char *fullpath;
|
|
|
|
HMODULE ret;
|
|
|
|
|
|
|
|
if (!sysdir) {
|
|
|
|
int size = 0, len;
|
|
|
|
do {
|
|
|
|
size = 3*size/2 + 512;
|
|
|
|
sysdir = sresize(sysdir, size, char);
|
|
|
|
len = GetSystemDirectory(sysdir, size);
|
|
|
|
} while (len >= size);
|
|
|
|
}
|
|
|
|
|
|
|
|
fullpath = dupcat(sysdir, "\\", libname, NULL);
|
|
|
|
ret = LoadLibrary(fullpath);
|
|
|
|
sfree(fullpath);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-07-22 07:11:39 +00:00
|
|
|
/*
|
|
|
|
* A tree234 containing mappings from system error codes to strings.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct errstring {
|
|
|
|
int error;
|
|
|
|
char *text;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int errstring_find(void *av, void *bv)
|
|
|
|
{
|
|
|
|
int *a = (int *)av;
|
|
|
|
struct errstring *b = (struct errstring *)bv;
|
|
|
|
if (*a < b->error)
|
|
|
|
return -1;
|
|
|
|
if (*a > b->error)
|
|
|
|
return +1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int errstring_compare(void *av, void *bv)
|
|
|
|
{
|
|
|
|
struct errstring *a = (struct errstring *)av;
|
|
|
|
return errstring_find(&a->error, bv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static tree234 *errstrings = NULL;
|
|
|
|
|
|
|
|
const char *win_strerror(int error)
|
|
|
|
{
|
|
|
|
struct errstring *es;
|
|
|
|
|
|
|
|
if (!errstrings)
|
|
|
|
errstrings = newtree234(errstring_compare);
|
|
|
|
|
|
|
|
es = find234(errstrings, &error, errstring_find);
|
|
|
|
|
|
|
|
if (!es) {
|
2013-11-22 19:41:43 +00:00
|
|
|
char msgtext[65536]; /* maximum size for FormatMessage is 64K */
|
2013-07-22 07:11:39 +00:00
|
|
|
|
|
|
|
es = snew(struct errstring);
|
|
|
|
es->error = error;
|
|
|
|
if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM |
|
|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error,
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
2013-11-22 19:41:43 +00:00
|
|
|
msgtext, lenof(msgtext)-1, NULL)) {
|
|
|
|
sprintf(msgtext,
|
2015-08-11 12:13:20 +00:00
|
|
|
"(unable to format: FormatMessage returned %u)",
|
|
|
|
(unsigned int)GetLastError());
|
2013-07-22 07:11:39 +00:00
|
|
|
} else {
|
2013-11-22 19:41:43 +00:00
|
|
|
int len = strlen(msgtext);
|
|
|
|
if (len > 0 && msgtext[len-1] == '\n')
|
|
|
|
msgtext[len-1] = '\0';
|
2013-07-22 07:11:39 +00:00
|
|
|
}
|
2013-11-22 19:41:43 +00:00
|
|
|
es->text = dupprintf("Error %d: %s", error, msgtext);
|
2013-07-22 07:11:39 +00:00
|
|
|
add234(errstrings, es);
|
|
|
|
}
|
|
|
|
|
|
|
|
return es->text;
|
|
|
|
}
|
|
|
|
|
2003-03-06 13:24:02 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
static FILE *debug_fp = NULL;
|
|
|
|
static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
|
|
|
|
static int debug_got_console = 0;
|
|
|
|
|
2015-05-15 10:15:42 +00:00
|
|
|
void dputs(const char *buf)
|
2003-03-06 13:24:02 +00:00
|
|
|
{
|
|
|
|
DWORD dw;
|
|
|
|
|
|
|
|
if (!debug_got_console) {
|
|
|
|
if (AllocConsole()) {
|
|
|
|
debug_got_console = 1;
|
|
|
|
debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!debug_fp) {
|
|
|
|
debug_fp = fopen("debug.log", "w");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debug_hdl != INVALID_HANDLE_VALUE) {
|
|
|
|
WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
|
|
|
|
}
|
|
|
|
fputs(buf, debug_fp);
|
|
|
|
fflush(debug_fp);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef MINEFIELD
|
|
|
|
/*
|
|
|
|
* Minefield - a Windows equivalent for Electric Fence
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define PAGESIZE 4096
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Design:
|
|
|
|
*
|
|
|
|
* We start by reserving as much virtual address space as Windows
|
|
|
|
* will sensibly (or not sensibly) let us have. We flag it all as
|
|
|
|
* invalid memory.
|
|
|
|
*
|
|
|
|
* Any allocation attempt is satisfied by committing one or more
|
|
|
|
* pages, with an uncommitted page on either side. The returned
|
|
|
|
* memory region is jammed up against the _end_ of the pages.
|
|
|
|
*
|
|
|
|
* Freeing anything causes instantaneous decommitment of the pages
|
|
|
|
* involved, so stale pointers are caught as soon as possible.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int minefield_initialised = 0;
|
|
|
|
static void *minefield_region = NULL;
|
|
|
|
static long minefield_size = 0;
|
|
|
|
static long minefield_npages = 0;
|
|
|
|
static long minefield_curpos = 0;
|
|
|
|
static unsigned short *minefield_admin = NULL;
|
|
|
|
static void *minefield_pages = NULL;
|
|
|
|
|
|
|
|
static void minefield_admin_hide(int hide)
|
|
|
|
{
|
|
|
|
int access = hide ? PAGE_NOACCESS : PAGE_READWRITE;
|
|
|
|
VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void minefield_init(void)
|
|
|
|
{
|
|
|
|
int size;
|
|
|
|
int admin_size;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (size = 0x40000000; size > 0; size = ((size >> 3) * 7) & ~0xFFF) {
|
|
|
|
minefield_region = VirtualAlloc(NULL, size,
|
|
|
|
MEM_RESERVE, PAGE_NOACCESS);
|
|
|
|
if (minefield_region)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
minefield_size = size;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Firstly, allocate a section of that to be the admin block.
|
|
|
|
* We'll need a two-byte field for each page.
|
|
|
|
*/
|
|
|
|
minefield_admin = minefield_region;
|
|
|
|
minefield_npages = minefield_size / PAGESIZE;
|
|
|
|
admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1);
|
|
|
|
minefield_npages = (minefield_size - admin_size) / PAGESIZE;
|
|
|
|
minefield_pages = (char *) minefield_region + admin_size;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Commit the admin region.
|
|
|
|
*/
|
|
|
|
VirtualAlloc(minefield_admin, minefield_npages * 2,
|
|
|
|
MEM_COMMIT, PAGE_READWRITE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark all pages as unused (0xFFFF).
|
|
|
|
*/
|
|
|
|
for (i = 0; i < minefield_npages; i++)
|
|
|
|
minefield_admin[i] = 0xFFFF;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Hide the admin region.
|
|
|
|
*/
|
|
|
|
minefield_admin_hide(1);
|
|
|
|
|
|
|
|
minefield_initialised = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void minefield_bomb(void)
|
|
|
|
{
|
|
|
|
div(1, *(int *) minefield_pages);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *minefield_alloc(int size)
|
|
|
|
{
|
|
|
|
int npages;
|
|
|
|
int pos, lim, region_end, region_start;
|
|
|
|
int start;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
npages = (size + PAGESIZE - 1) / PAGESIZE;
|
|
|
|
|
|
|
|
minefield_admin_hide(0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search from current position until we find a contiguous
|
|
|
|
* bunch of npages+2 unused pages.
|
|
|
|
*/
|
|
|
|
pos = minefield_curpos;
|
|
|
|
lim = minefield_npages;
|
|
|
|
while (1) {
|
|
|
|
/* Skip over used pages. */
|
|
|
|
while (pos < lim && minefield_admin[pos] != 0xFFFF)
|
|
|
|
pos++;
|
|
|
|
/* Count unused pages. */
|
|
|
|
start = pos;
|
|
|
|
while (pos < lim && pos - start < npages + 2 &&
|
|
|
|
minefield_admin[pos] == 0xFFFF)
|
|
|
|
pos++;
|
|
|
|
if (pos - start == npages + 2)
|
|
|
|
break;
|
|
|
|
/* If we've reached the limit, reset the limit or stop. */
|
|
|
|
if (pos >= lim) {
|
|
|
|
if (lim == minefield_npages) {
|
|
|
|
/* go round and start again at zero */
|
|
|
|
lim = minefield_curpos;
|
|
|
|
pos = 0;
|
|
|
|
} else {
|
|
|
|
minefield_admin_hide(1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
minefield_curpos = pos - 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have npages+2 unused pages starting at start. We leave
|
|
|
|
* the first and last of these alone and use the rest.
|
|
|
|
*/
|
|
|
|
region_end = (start + npages + 1) * PAGESIZE;
|
|
|
|
region_start = region_end - size;
|
|
|
|
/* FIXME: could align here if we wanted */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the admin region.
|
|
|
|
*/
|
|
|
|
for (i = start + 2; i < start + npages + 1; i++)
|
|
|
|
minefield_admin[i] = 0xFFFE; /* used but no region starts here */
|
|
|
|
minefield_admin[start + 1] = region_start % PAGESIZE;
|
|
|
|
|
|
|
|
minefield_admin_hide(1);
|
|
|
|
|
|
|
|
VirtualAlloc((char *) minefield_pages + region_start, size,
|
|
|
|
MEM_COMMIT, PAGE_READWRITE);
|
|
|
|
return (char *) minefield_pages + region_start;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void minefield_free(void *ptr)
|
|
|
|
{
|
|
|
|
int region_start, i, j;
|
|
|
|
|
|
|
|
minefield_admin_hide(0);
|
|
|
|
|
|
|
|
region_start = (char *) ptr - (char *) minefield_pages;
|
|
|
|
i = region_start / PAGESIZE;
|
|
|
|
if (i < 0 || i >= minefield_npages ||
|
|
|
|
minefield_admin[i] != region_start % PAGESIZE)
|
|
|
|
minefield_bomb();
|
|
|
|
for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) {
|
|
|
|
minefield_admin[j] = 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT);
|
|
|
|
|
|
|
|
minefield_admin_hide(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int minefield_get_size(void *ptr)
|
|
|
|
{
|
|
|
|
int region_start, i, j;
|
|
|
|
|
|
|
|
minefield_admin_hide(0);
|
|
|
|
|
|
|
|
region_start = (char *) ptr - (char *) minefield_pages;
|
|
|
|
i = region_start / PAGESIZE;
|
|
|
|
if (i < 0 || i >= minefield_npages ||
|
|
|
|
minefield_admin[i] != region_start % PAGESIZE)
|
|
|
|
minefield_bomb();
|
|
|
|
for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++);
|
|
|
|
|
|
|
|
minefield_admin_hide(1);
|
|
|
|
|
|
|
|
return j * PAGESIZE - region_start;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *minefield_c_malloc(size_t size)
|
|
|
|
{
|
|
|
|
if (!minefield_initialised)
|
|
|
|
minefield_init();
|
|
|
|
return minefield_alloc(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void minefield_c_free(void *p)
|
|
|
|
{
|
|
|
|
if (!minefield_initialised)
|
|
|
|
minefield_init();
|
|
|
|
minefield_free(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* realloc _always_ moves the chunk, for rapid detection of code
|
|
|
|
* that assumes it won't.
|
|
|
|
*/
|
|
|
|
void *minefield_c_realloc(void *p, size_t size)
|
|
|
|
{
|
|
|
|
size_t oldsize;
|
|
|
|
void *q;
|
|
|
|
if (!minefield_initialised)
|
|
|
|
minefield_init();
|
|
|
|
q = minefield_alloc(size);
|
|
|
|
oldsize = minefield_get_size(p);
|
|
|
|
memcpy(q, p, (oldsize < size ? oldsize : size));
|
|
|
|
minefield_free(p);
|
|
|
|
return q;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* MINEFIELD */
|
2011-10-01 17:38:59 +00:00
|
|
|
|
|
|
|
FontSpec *fontspec_new(const char *name,
|
|
|
|
int bold, int height, int charset)
|
|
|
|
{
|
|
|
|
FontSpec *f = snew(FontSpec);
|
|
|
|
f->name = dupstr(name);
|
|
|
|
f->isbold = bold;
|
|
|
|
f->height = height;
|
|
|
|
f->charset = charset;
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
FontSpec *fontspec_copy(const FontSpec *f)
|
|
|
|
{
|
|
|
|
return fontspec_new(f->name, f->isbold, f->height, f->charset);
|
|
|
|
}
|
|
|
|
void fontspec_free(FontSpec *f)
|
|
|
|
{
|
|
|
|
sfree(f->name);
|
|
|
|
sfree(f);
|
|
|
|
}
|
2018-05-24 09:48:20 +00:00
|
|
|
void fontspec_serialise(BinarySink *bs, FontSpec *f)
|
2011-10-01 17:38:59 +00:00
|
|
|
{
|
2018-05-24 09:48:20 +00:00
|
|
|
put_asciz(bs, f->name);
|
|
|
|
put_uint32(bs, f->isbold);
|
|
|
|
put_uint32(bs, f->height);
|
|
|
|
put_uint32(bs, f->charset);
|
2011-10-01 17:38:59 +00:00
|
|
|
}
|
|
|
|
FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used)
|
|
|
|
{
|
|
|
|
char *data = (char *)vdata;
|
|
|
|
char *end;
|
|
|
|
if (maxsize < 13)
|
|
|
|
return NULL;
|
|
|
|
end = memchr(data, '\0', maxsize-12);
|
|
|
|
if (!end)
|
|
|
|
return NULL;
|
|
|
|
end++;
|
|
|
|
*used = end - data + 12;
|
|
|
|
return fontspec_new(data,
|
|
|
|
GET_32BIT_MSB_FIRST(end),
|
|
|
|
GET_32BIT_MSB_FIRST(end + 4),
|
|
|
|
GET_32BIT_MSB_FIRST(end + 8));
|
|
|
|
}
|
2018-02-07 07:22:18 +00:00
|
|
|
|
|
|
|
int open_for_write_would_lose_data(const Filename *fn)
|
|
|
|
{
|
2018-02-07 20:05:32 +00:00
|
|
|
WIN32_FILE_ATTRIBUTE_DATA attrs;
|
|
|
|
if (!GetFileAttributesEx(fn->path, GetFileExInfoStandard, &attrs)) {
|
|
|
|
/*
|
|
|
|
* Generally, if we don't identify a specific reason why we
|
|
|
|
* should return true from this function, we return false, and
|
|
|
|
* let the subsequent attempt to open the file for real give a
|
|
|
|
* more useful error message.
|
|
|
|
*/
|
2018-02-07 07:22:18 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2018-02-07 20:05:32 +00:00
|
|
|
if (attrs.dwFileAttributes & (FILE_ATTRIBUTE_DEVICE |
|
|
|
|
FILE_ATTRIBUTE_DIRECTORY)) {
|
|
|
|
/*
|
|
|
|
* File is something other than an ordinary disk file, so
|
|
|
|
* opening it for writing will not cause truncation. (It may
|
|
|
|
* not _succeed_ either, but that's not our problem here!)
|
|
|
|
*/
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (attrs.nFileSizeHigh == 0 && attrs.nFileSizeLow == 0) {
|
|
|
|
/*
|
|
|
|
* File is zero-length (or may be a named pipe, which
|
|
|
|
* dwFileAttributes can't tell apart from a regular file), so
|
|
|
|
* opening it for writing won't truncate any data away because
|
|
|
|
* there's nothing to truncate anyway.
|
|
|
|
*/
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
2018-02-07 07:22:18 +00:00
|
|
|
}
|