blob: 8132c4af881bd359de0f34eafc785168cdf05c36 [file] [log] [blame]
/* Functions used by the Windows port of libgccjit.
Copyright (C) 2020-2021 Free Software Foundation, Inc.
Contributed by Nicolas Bertolo <nicolasbertolo@gmail.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
/* Required for rand_s */
#define _CRT_RAND_S
#include <cstdio>
#include <cstdint>
#include "jit-w32.h"
#include "libiberty.h"
#include <accctrl.h>
#include <aclapi.h>
namespace gcc {
namespace jit {
void
print_last_error (void)
{
LPSTR psz = NULL;
DWORD dwErrorCode;
dwErrorCode = GetLastError();
const DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL,
dwErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPSTR>(&psz),
0,
NULL);
if (cchMsg > 0)
{
fprintf (stderr, "%s\n", psz);
LocalFree (psz);
}
else
{
fprintf (stderr, "Failed to retrieve error message string for error %lu\n",
dwErrorCode);
}
}
/* Helper function used for getting the SID belonging to the current user. */
static TOKEN_USER*
get_TOKEN_USER_current_user ()
{
TOKEN_USER *result = NULL;
HANDLE process_token = INVALID_HANDLE_VALUE;
DWORD token_user_info_len;
TOKEN_USER *token_user = NULL;
/* Get current process access token. */
if (!OpenProcessToken (GetCurrentProcess (), TOKEN_READ,
&process_token))
return NULL;
/* Get necessary buffer size. */
if (!GetTokenInformation(process_token, TokenUser, NULL, 0, &token_user_info_len)
&& GetLastError() != ERROR_INSUFFICIENT_BUFFER)
goto cleanup;
token_user = (TOKEN_USER*) new char[token_user_info_len];
/* Get info about the user of the process */
if (!GetTokenInformation (process_token, TokenUser, token_user,
token_user_info_len, &token_user_info_len))
goto cleanup;
result = token_user;
cleanup:
if (process_token != INVALID_HANDLE_VALUE)
CloseHandle(process_token);
if (token_user != NULL && result == NULL)
delete[] (char*)token_user;
return result;
}
/* Helper function to create a directory with permissions such that only the
current user can access it. */
static bool
create_directory_for_current_user (const char * path)
{
PACL pACL = NULL;
EXPLICIT_ACCESS ea;
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR SD;
DWORD dwRes;
bool result = true;
TOKEN_USER *token_user = NULL;
token_user = get_TOKEN_USER_current_user();
if (!token_user)
return false;
memset (&ea, 0, sizeof (EXPLICIT_ACCESS));
ea.grfAccessPermissions = GENERIC_ALL; /* Access to all. */
ea.grfAccessMode = SET_ACCESS; /* Set access and revoke everything else. */
/* This is necessary for the Windows Explorer GUI to show the correct tick
boxes in the "Security" tab. */
ea.grfInheritance = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
ea.Trustee.ptstrName = (char*) token_user->User.Sid;
/* Create a new ACL that contains the new ACEs. */
dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL);
if (dwRes != ERROR_SUCCESS)
return false;
if (!InitializeSecurityDescriptor (&SD,
SECURITY_DESCRIPTOR_REVISION))
goto cleanup;
/* Add the ACL to the security descriptor. */
if (!SetSecurityDescriptorDacl (&SD,
TRUE, /* use pACL */
pACL,
FALSE)) /* not a default DACL */
goto cleanup;
/* Initialize a security attributes structure. */
sa.nLength = sizeof (SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &SD;
sa.bInheritHandle = FALSE;
/* Finally create the directory */
if (!CreateDirectoryA (path, &sa))
result = false;
cleanup:
if (pACL)
LocalFree (pACL);
if (token_user)
delete[] (char*)token_user;
return result;
}
char *
win_mkdtemp (void)
{
char lpTempPathBuffer[MAX_PATH];
/* Gets the temp path env string (no guarantee it's a valid path). */
DWORD dwRetVal = GetTempPath (MAX_PATH, lpTempPathBuffer);
if (dwRetVal > MAX_PATH || (dwRetVal == 0))
{
print_last_error ();
return NULL;
}
/* Check that the directory actually exists. */
DWORD dwAttrib = GetFileAttributes (lpTempPathBuffer);
bool temp_path_exists = (dwAttrib != INVALID_FILE_ATTRIBUTES
&& (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
if (!temp_path_exists)
{
fprintf (stderr, "Path returned by GetTempPath does not exist: %s\n",
lpTempPathBuffer);
}
/* Make sure there is enough space in the buffer for the prefix and random
number.*/
int temp_path_buffer_len = dwRetVal;
const int appended_len = strlen ("\\libgccjit-123456");
if (temp_path_buffer_len + appended_len + 1 >= MAX_PATH)
{
fprintf (stderr, "Temporary file path too long for generation of random"
" directories: %s", lpTempPathBuffer);
}
/* This is all the space we have in the buffer to store the random number and
prefix. */
int extraspace = MAX_PATH - temp_path_buffer_len - 1;
int tries;
const int max_tries = 1000;
for (tries = 0; tries < max_tries; ++tries)
{
/* Get a random number in [0; UINT_MAX]. */
unsigned int rand_num;
if (rand_s (&rand_num) != 0)
{
fprintf (stderr,
"Failed to create a random number using rand_s(): %s\n",
_strerror (NULL));
return NULL;
}
/* Create 6 digits random number. */
rand_num = ((double)rand_num / ((double) UINT_MAX + 1 ) * 1000000);
/* Copy the prefix and random number to the buffer. */
snprintf (&lpTempPathBuffer[temp_path_buffer_len], extraspace,
"\\libgccjit-%06u", rand_num);
if (create_directory_for_current_user (lpTempPathBuffer))
break; // success!
/* If we can't create the directory because we got unlucky and the
directory already exists retry, otherwise fail. */
if (GetLastError () != ERROR_ALREADY_EXISTS)
{
print_last_error ();
return NULL;
}
}
if (tries == max_tries)
{
fprintf (stderr, "Failed to create a random directory in %s\n",
lpTempPathBuffer);
return NULL;
}
{
int allocate_len = temp_path_buffer_len + appended_len + 1;
char * result = XNEWVEC (char, allocate_len);
strcpy (result, lpTempPathBuffer);
return result;
}
}
}
}