PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/referencesource/System.Data/System/Data/SqlClient/LocalDBAPI.cs

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