summaryrefslogtreecommitdiff
path: root/src/classlibnative/nls/nlstable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/classlibnative/nls/nlstable.cpp')
-rw-r--r--src/classlibnative/nls/nlstable.cpp259
1 files changed, 259 insertions, 0 deletions
diff --git a/src/classlibnative/nls/nlstable.cpp b/src/classlibnative/nls/nlstable.cpp
new file mode 100644
index 0000000000..dd395153d4
--- /dev/null
+++ b/src/classlibnative/nls/nlstable.cpp
@@ -0,0 +1,259 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include <winwrap.h>
+#include <excep.h> // For COMPlusThrow
+#include <appdomain.hpp>
+#include <assembly.hpp>
+#include "nlstable.h" // Class declaration
+
+#if FEATURE_CODEPAGES_FILE
+
+/*=================================CreateSharedMemoryMapping==================================
+**Action: Create a file mapping object which can be shared among different users under Windows NT/2000.
+** Actually its just a memory mapped section of the swapfile.
+**Returns: The file mapping handle. NULL if any error happens.
+**Arguments:
+** pMappingName the name of the file mapping object.
+** iSize Size to use
+**Exceptions:
+**Note:
+** This function creates a DACL which grants GENERIC_ALL access to members of the "Everyone" group.
+** Then create a security descriptor using this DACL. Finally, use this SA to create the file mapping object.
+** WARNING:
+** This creates a shared file or shared paged memory section (if hFile == INVALID_HANDLE_VALUE) that is shared machine-wide
+** Therefore for side-by-side to work, the mapping names must be unique per version!
+** We utilize this feature for code pages in case it hasn't changed across versions we can still reuse the
+** tables, but it seems suspicious for other applications (as commented in MapDataFile below)
+==============================================================================*/
+// static method
+HANDLE NLSTable::CreateSharedMemoryMapping(const LPCWSTR pMappingName, const int iSize ) {
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(iSize > 0);
+ PRECONDITION(CheckPointer(pMappingName));
+ } CONTRACTL_END;
+
+ HANDLE hFileMap = NULL;
+
+ SECURITY_DESCRIPTOR sd ;
+ SECURITY_ATTRIBUTES sa ;
+
+ //
+ // Create the sid for the Everyone group.
+ //
+ SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
+ PSID pSID = NULL;
+ int nSidSize;
+
+ PACL pDACL = NULL;
+ int nAclSize;
+
+ CQuickBytes newBuffer;
+
+ if (!AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSID)) {
+ goto ErrorExit;
+ }
+
+ nSidSize = GetLengthSid(pSID);
+
+ //
+ // Create Discretionary Access Control List (DACL).
+ //
+
+ // First calculate the size of the DACL, since this is a linked-list like structure which contains one or more
+ // ACE (access control entry)
+ nAclSize = sizeof(ACL) // the header structure of ACL
+ + sizeof(ACCESS_ALLOWED_ACE) + nSidSize; // and one "access allowed ACE".
+
+ // We know the size needed for DACL now, so create it.
+ // An exception is thrown if OOM happens.
+ pDACL = (PACL) (newBuffer.AllocThrows(nAclSize));
+ if(!InitializeAcl( pDACL, nAclSize, ACL_REVISION ))
+ goto ErrorExit;
+
+ // Add the "access allowed ACE", meaning:
+ // we will allow members of the "Everyone" group to have SECTION_MAP_READ | SECTION_QUERY access to the file mapping object.
+ // for memory sections the creator will still be allowed to create it.
+ if(!AddAccessAllowedAce( pDACL, ACL_REVISION, SECTION_MAP_READ | SECTION_QUERY, pSID ))
+ goto ErrorExit;
+
+ //
+ // Create Security descriptor (SD).
+ //
+ if(!InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION ))
+ goto ErrorExit;
+ // Set the previously created DACL to this SD.
+ if(!SetSecurityDescriptorDacl( &sd, TRUE, pDACL, FALSE ))
+ goto ErrorExit;
+
+ // Create Security Attribute (SA).
+ sa.nLength = sizeof( sa ) ;
+ sa.bInheritHandle = TRUE ;
+ sa.lpSecurityDescriptor = &sd ;
+
+ //
+ // Finally, create the file mapping using the SA.
+ //
+
+ // If we are on Windows 2000 or later, try to open it in global namespace. The \global namespace is ignored if
+ // Terminal service is not running.
+ WCHAR globalSectionName[MAX_LONGPATH];
+ wcscpy_s(globalSectionName, COUNTOF(globalSectionName), W("Global\\"));
+ if (wcslen(pMappingName) + wcslen(globalSectionName) >= MAX_LONGPATH) {
+ goto ErrorExit;
+ }
+ wcscat_s(globalSectionName, COUNTOF(globalSectionName), pMappingName);
+
+ // Try to create the section in the Global\ namespace. The CreateFileMapping() will ignore Global\ namespace if Terminal Service
+ // is not running.
+ hFileMap = WszCreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, iSize, globalSectionName);
+
+ // If not allowed to be global (like terminal server) or not WinNT, then open it in local namespace.
+ if (hFileMap == NULL)
+ {
+ // Not WinNT or access denied for Global\ namespace, try the local namespace. When Terminal service is running, the Local\ namespace
+ // means the namespace "Sessions\<n>\BasedNamedObjects".
+ hFileMap = WszCreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, iSize, pMappingName);
+ }
+
+ErrorExit:
+ if(pSID)
+ FreeSid( pSID );
+
+ // If still not allowed, try building one with no name
+ if (hFileMap == NULL)
+ {
+ hFileMap = WszCreateFileMapping(
+ INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, iSize, NULL);
+ }
+
+ return (hFileMap) ;
+}
+
+/*=================================OpenOrCreateMemoryMapping==================================
+**Action: Opens an existing memory mapped object, or creates a new one (by calling above fn).
+** Worst case just allocate some memory.
+**Returns: The pointer to our memory. NULL if any error happens.
+**Arguments:
+** pMappingName the name of the file mapping object.
+** iSize Size to use
+**Exceptions:
+**
+**IMPORTANT:
+** Memory mapped sections are cleared when set. We expect the caller to set the last int
+** to a non-zero value, so we test this flag. If it is still zero when we open it, we
+** assume that we've gotten a result in an unfinished state and allocate a new one instead
+** of trying to use the one with the zeros.
+**
+**Note:
+** This function creates a DACL which grants GENERIC_ALL access to members of the "Everyone" group.
+** Then create a security descriptor using this DACL. Finally, use this SA to create the file mapping object.
+** WARNING:
+** This creates a shared file or shared paged memory section (if hFile == INVALID_HANDLE_VALUE) that is shared machine-wide
+** Therefore for side-by-side to work, the mapping names must be unique per version!
+** We utilize this feature for code pages in case it hasn't changed across versions we can still reuse the
+** tables, but it seems suspicious for other applications (as commented in MapDataFile below)
+==============================================================================*/
+PBYTE NLSTable::OpenOrCreateMemoryMapping(const LPCWSTR pMappingName, const int iSize, HANDLE* mappedFile)
+{
+ CONTRACTL
+ {
+ THROWS;
+ DISABLED(GC_TRIGGERS); //
+ MODE_ANY;
+ PRECONDITION(iSize % 4 == 0);
+ PRECONDITION(iSize > 0);
+ PRECONDITION(CheckPointer(pMappingName));
+ PRECONDITION(CheckPointer(mappedFile));
+ } CONTRACTL_END;
+
+ _ASSERTE(pMappingName != NULL); // Must have a string name.
+ _ASSERTE(iSize > 0); // Pointless to have <= 0 allocation
+ _ASSERTE(iSize % 4 == 0); // Need 4 byte alignment for flag check
+
+ LPVOID pResult = NULL;
+
+ // Try creating/opening it.
+ HANDLE hMap = NULL;
+
+ *mappedFile = hMap;
+ // Calls into OS a lot, should switch to preemp mode
+ GCX_PREEMP();
+
+ // First try opening it. It might already be in existence
+ // The assumption here is that it's rare that we will hit the cases where two or more threads are trying to create
+ // the named section at the same time. Therefore, the following code does not use critical section or mutex trying
+ // to synchornize different threads.
+
+ // Try to open it in global namespace. The global\ namespace is ignored if terminal service is not running.
+ WCHAR globalSectionName[MAX_LONGPATH];
+ wcscpy_s(globalSectionName, COUNTOF(globalSectionName), W("Global\\"));
+ if (wcslen(pMappingName) + wcslen(globalSectionName) >= MAX_LONGPATH)
+ return NULL;
+
+ wcscat_s(globalSectionName, COUNTOF(globalSectionName), pMappingName);
+
+ hMap = WszOpenFileMapping(FILE_MAP_READ, TRUE, globalSectionName);
+ if (hMap == NULL) {
+ // If access is denied for global\namespace or the name is not opened in global namespace, try the local namespace.
+ // Also if we're rotor or win 9x.
+ hMap = WszOpenFileMapping(FILE_MAP_READ, TRUE, pMappingName);
+ }
+
+ if (hMap != NULL) {
+ // We got a section, map a view, READ ONLY!
+ pResult = MapViewOfFile( hMap, FILE_MAP_READ, 0, 0, 0);
+
+ // Anything found?
+ if (pResult != NULL)
+ {
+ // Make sure our result is allocated. We expect a non-0 flag to be set for last int of our section
+ const int* pFlag = (int*)(((BYTE*)pResult) + iSize - 4);
+ if (*pFlag != 0)
+ {
+ *mappedFile = hMap;
+ // Found a valid already opened section!
+ return (PBYTE)pResult;
+ }
+
+ // Couldn't find it, unmap it.
+ UnmapViewOfFile(pResult);
+ pResult = NULL;
+ }
+
+ // We can't use this one, so close it
+ CloseHandle(hMap);
+ hMap = NULL;
+ }
+
+ // Didn't get a section, try to create one, NT/XP/.Net gets security permissions, 9X doesn't,
+ // but our helper fn takes care of that for us.
+ hMap = NLSTable::CreateSharedMemoryMapping(pMappingName, iSize);
+
+ // Were we successfull?
+ if (hMap != NULL)
+ {
+ // We have hMap, try to get our section
+ pResult = MapViewOfFile( hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+
+ // Don't close hMap unless we aren't using it.
+ // That confuses the mapping stuff and we lose the name, it'll close when runtime shuts down.
+ if (pResult == NULL)
+ {
+ CloseHandle(hMap);
+ hMap = NULL;
+ }
+ // There is no need to zero out the mapCodePageCached field, since the initial contents of the pages in the file mapping object are zero.
+
+ *mappedFile = hMap;
+ }
+
+ return (PBYTE)pResult;
+}
+
+#endif