| /* Functions used by the Windows port of libgccjit. |
| Copyright (C) 2020-2022 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; |
| } |
| } |
| } |
| } |