1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
|
//------------------------------------------------------------------------------
// <copyright file="LocalDB.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">antonam</owner>
//------------------------------------------------------------------------------
namespace System.Data
{
using System.Configuration;
using System.Threading;
using System.Runtime.InteropServices;
using System.Data.Common;
using System.Globalization;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using System.Diagnostics;
using System.Security;
using System.Security.Permissions;
internal static class LocalDBAPI
{
const string const_localDbPrefix = @"(localdb)\";
const string const_partialTrustFlagKey = "ALLOW_LOCALDB_IN_PARTIAL_TRUST";
static PermissionSet _fullTrust = null;
static bool _partialTrustFlagChecked = false;
static bool _partialTrustAllowed = false;
// check if name is in format (localdb)\<InstanceName - not empty> and return instance name if it is
internal static string GetLocalDbInstanceNameFromServerName(string serverName)
{
if (serverName == null)
return null;
serverName = serverName.TrimStart(); // it can start with spaces if specified in quotes
if (!serverName.StartsWith(const_localDbPrefix, StringComparison.OrdinalIgnoreCase))
return null;
string instanceName = serverName.Substring(const_localDbPrefix.Length).Trim();
if (instanceName.Length == 0)
return null;
else
return instanceName;
}
#if !MONO
internal static void ReleaseDLLHandles()
{
s_userInstanceDLLHandle = IntPtr.Zero;
s_localDBFormatMessage = null;
s_localDBCreateInstance = null;
}
//This is copy of handle that SNI maintains, so we are responsible for freeing it - therefore there we are not using SafeHandle
static IntPtr s_userInstanceDLLHandle = IntPtr.Zero;
static object s_dllLock = new object();
static IntPtr UserInstanceDLLHandle
{
get
{
if (s_userInstanceDLLHandle==IntPtr.Zero)
{
bool lockTaken = false;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
Monitor.Enter(s_dllLock, ref lockTaken);
if (s_userInstanceDLLHandle == IntPtr.Zero)
{
SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_LOCALDB_HMODULE, ref s_userInstanceDLLHandle);
if (s_userInstanceDLLHandle != IntPtr.Zero)
{
Bid.Trace("<sc.LocalDBAPI.UserInstanceDLLHandle> LocalDB - handle obtained");
}
else
{
SNINativeMethodWrapper.SNI_Error sniError = new SNINativeMethodWrapper.SNI_Error();
SNINativeMethodWrapper.SNIGetLastError(sniError);
throw CreateLocalDBException(errorMessage: Res.GetString("LocalDB_FailedGetDLLHandle"), sniError: (int)sniError.sniError);
}
}
}
finally
{
if (lockTaken)
Monitor.Exit(s_dllLock);
}
}
return s_userInstanceDLLHandle;
}
}
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int LocalDBCreateInstanceDelegate([MarshalAs(UnmanagedType.LPWStr)] string version, [MarshalAs(UnmanagedType.LPWStr)] string instance, UInt32 flags);
static LocalDBCreateInstanceDelegate s_localDBCreateInstance = null;
static LocalDBCreateInstanceDelegate LocalDBCreateInstance
{
get
{
if (s_localDBCreateInstance==null)
{
bool lockTaken = false;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
Monitor.Enter(s_dllLock, ref lockTaken);
if (s_localDBCreateInstance == null)
{
IntPtr functionAddr = SafeNativeMethods.GetProcAddress(UserInstanceDLLHandle, "LocalDBCreateInstance");
if (functionAddr == IntPtr.Zero)
{
int hResult=Marshal.GetLastWin32Error();
Bid.Trace("<sc.LocalDBAPI.LocalDBCreateInstance> GetProcAddress for LocalDBCreateInstance error 0x{%X}",hResult);
throw CreateLocalDBException(errorMessage: Res.GetString("LocalDB_MethodNotFound"));
}
s_localDBCreateInstance = (LocalDBCreateInstanceDelegate)Marshal.GetDelegateForFunctionPointer(functionAddr, typeof(LocalDBCreateInstanceDelegate));
}
}
finally
{
if (lockTaken)
Monitor.Exit(s_dllLock);
}
}
return s_localDBCreateInstance;
}
}
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl,CharSet=CharSet.Unicode)]
private delegate int LocalDBFormatMessageDelegate(int hrLocalDB, UInt32 dwFlags, UInt32 dwLanguageId, StringBuilder buffer, ref UInt32 buflen);
static LocalDBFormatMessageDelegate s_localDBFormatMessage = null;
static LocalDBFormatMessageDelegate LocalDBFormatMessage
{
get
{
if (s_localDBFormatMessage==null)
{
bool lockTaken = false;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
Monitor.Enter(s_dllLock, ref lockTaken);
if (s_localDBFormatMessage == null)
{
IntPtr functionAddr = SafeNativeMethods.GetProcAddress(UserInstanceDLLHandle, "LocalDBFormatMessage");
if (functionAddr == IntPtr.Zero)
{
// SNI checks for LocalDBFormatMessage during DLL loading, so it is practically impossibe to get this error.
int hResult=Marshal.GetLastWin32Error();
Bid.Trace("<sc.LocalDBAPI.LocalDBFormatMessage> GetProcAddress for LocalDBFormatMessage error 0x{%X}", hResult);
throw CreateLocalDBException(errorMessage: Res.GetString("LocalDB_MethodNotFound"));
}
s_localDBFormatMessage = (LocalDBFormatMessageDelegate)Marshal.GetDelegateForFunctionPointer(functionAddr, typeof(LocalDBFormatMessageDelegate));
}
}
finally
{
if (lockTaken)
Monitor.Exit(s_dllLock);
}
}
return s_localDBFormatMessage;
}
}
const UInt32 const_LOCALDB_TRUNCATE_ERR_MESSAGE = 1;// flag for LocalDBFormatMessage that indicates that message can be truncated if it does not fit in the buffer
const int const_ErrorMessageBufferSize = 1024; // Buffer size for Local DB error message, according to Serverless team, 1K will be enough for all messages
internal static string GetLocalDBMessage(int hrCode)
{
Debug.Assert(hrCode < 0, "HRCode does not indicate error");
try
{
StringBuilder buffer = new StringBuilder((int)const_ErrorMessageBufferSize);
UInt32 len = (UInt32)buffer.Capacity;
// First try for current culture
int hResult=LocalDBFormatMessage(hrLocalDB: hrCode, dwFlags: const_LOCALDB_TRUNCATE_ERR_MESSAGE, dwLanguageId: (UInt32)CultureInfo.CurrentCulture.LCID,
buffer: buffer, buflen: ref len);
if (hResult>=0)
return buffer.ToString();
else
{
// Message is not available for current culture, try default
buffer = new StringBuilder((int)const_ErrorMessageBufferSize);
len = (UInt32) buffer.Capacity;
hResult=LocalDBFormatMessage(hrLocalDB: hrCode, dwFlags: const_LOCALDB_TRUNCATE_ERR_MESSAGE, dwLanguageId: 0 /* thread locale with fallback to English */,
buffer: buffer, buflen: ref len);
if (hResult >= 0)
return buffer.ToString();
else
return string.Format(CultureInfo.CurrentCulture, "{0} (0x{1:X}).", Res.GetString("LocalDB_UnobtainableMessage"), hResult);
}
}
catch (SqlException exc)
{
return string.Format(CultureInfo.CurrentCulture, "{0} ({1}).", Res.GetString("LocalDB_UnobtainableMessage"), exc.Message);
}
}
static SqlException CreateLocalDBException(string errorMessage, string instance = null, int localDbError = 0, int sniError = 0)
{
Debug.Assert((localDbError == 0) || (sniError == 0), "LocalDB error and SNI error cannot be specified simultaneously");
Debug.Assert(!string.IsNullOrEmpty(errorMessage), "Error message should not be null or empty");
SqlErrorCollection collection = new SqlErrorCollection();
int errorCode = (localDbError == 0) ? sniError : localDbError;
if (sniError!=0)
{
string sniErrorMessage = SQL.GetSNIErrorMessage(sniError);
errorMessage = String.Format((IFormatProvider)null, "{0} (error: {1} - {2})",
errorMessage, sniError, sniErrorMessage);
}
collection.Add(new SqlError(errorCode, 0, TdsEnums.FATAL_ERROR_CLASS, instance, errorMessage, null, 0));
if (localDbError != 0)
collection.Add(new SqlError(errorCode, 0, TdsEnums.FATAL_ERROR_CLASS, instance, GetLocalDBMessage(localDbError), null, 0));
SqlException exc = SqlException.CreateException(collection, null);
exc._doNotReconnect = true;
return exc;
}
private class InstanceInfo
{
internal InstanceInfo(string version)
{
this.version = version;
this.created = false;
}
internal readonly string version;
internal bool created;
}
static object s_configLock=new object();
static Dictionary<string, InstanceInfo> s_configurableInstances = null;
internal static void DemandLocalDBPermissions()
{
if (!_partialTrustAllowed)
{
if (!_partialTrustFlagChecked)
{
object partialTrustFlagValue = AppDomain.CurrentDomain.GetData(const_partialTrustFlagKey);
if (partialTrustFlagValue != null && partialTrustFlagValue is bool)
{
_partialTrustAllowed = (bool)partialTrustFlagValue;
}
_partialTrustFlagChecked = true;
if (_partialTrustAllowed)
{
return;
}
}
if (_fullTrust == null)
{
_fullTrust = new NamedPermissionSet("FullTrust");
}
_fullTrust.Demand();
}
}
internal static void AssertLocalDBPermissions()
{
_partialTrustAllowed = true;
}
internal static void CreateLocalDBInstance(string instance)
{
DemandLocalDBPermissions();
if (s_configurableInstances == null)
{
// load list of instances from configuration, mark them as not created
bool lockTaken = false;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
Monitor.Enter(s_configLock, ref lockTaken);
if (s_configurableInstances == null)
{
Dictionary<string, InstanceInfo> tempConfigurableInstances = new Dictionary<string, InstanceInfo>(StringComparer.OrdinalIgnoreCase);
#if !NO_CONFIGURATION
object section = PrivilegedConfigurationManager.GetSection("system.data.localdb");
if (section != null) // if no section just skip creation
{
// validate section type
LocalDBConfigurationSection configSection = section as LocalDBConfigurationSection;
if (configSection == null)
throw CreateLocalDBException(errorMessage: Res.GetString("LocalDB_BadConfigSectionType"));
foreach (LocalDBInstanceElement confElement in configSection.LocalDbInstances)
{
Debug.Assert(confElement.Name != null && confElement.Version != null, "Both name and version should not be null");
tempConfigurableInstances.Add(confElement.Name.Trim(), new InstanceInfo(confElement.Version.Trim()));
}
}
else
#endif
Bid.Trace( "<sc.LocalDBAPI.CreateLocalDBInstance> No system.data.localdb section found in configuration");
s_configurableInstances = tempConfigurableInstances;
}
}
finally
{
if (lockTaken)
Monitor.Exit(s_configLock);
}
}
InstanceInfo instanceInfo = null;
if (!s_configurableInstances.TryGetValue(instance,out instanceInfo))
return; // instance name was not in the config
if (instanceInfo.created)
return; // instance has already been created
Debug.Assert(!instance.Contains("\0"), "Instance name should contain embedded nulls");
if (instanceInfo.version.Contains("\0"))
throw CreateLocalDBException(errorMessage: Res.GetString("LocalDB_InvalidVersion"), instance: instance);
// LocalDBCreateInstance is thread- and cross-process safe method, it is OK to call from two threads simultaneously
int hr = LocalDBCreateInstance(instanceInfo.version, instance, flags: 0);
Bid.Trace("<sc.LocalDBAPI.CreateLocalDBInstance> Starting creation of instance %ls version %ls", instance, instanceInfo.version);
if (hr < 0)
throw CreateLocalDBException(errorMessage: Res.GetString("LocalDB_CreateFailed"), instance: instance, localDbError: hr);
Bid.Trace("<sc.LocalDBAPI.CreateLocalDBInstance> Finished creation of instance %ls", instance);
instanceInfo.created=true; // mark instance as created
} // CreateLocalDbInstance
#endif
}
}
|