7c847edace31b882d0681e4a314a13b90716f227
[mono.git] / mcs / class / referencesource / System.Data / System / Data / SQLTypes / UnsafeNativeMethods.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SafeNativeMethods.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 // <owner current="true" primary="false">Microsoft</owner>
8 //------------------------------------------------------------------------------
9
10 using System;
11 using System.Diagnostics;
12 using System.Text;
13 using System.Runtime.InteropServices;
14 using System.Security;
15 using Microsoft.Win32.SafeHandles;
16 using System.Data.Common;
17 using System.Runtime.Versioning;
18 using System.Diagnostics.CodeAnalysis;
19
20 namespace System.Data.SqlTypes 
21 {
22     [SuppressUnmanagedCodeSecurity]
23     internal static class UnsafeNativeMethods
24     {
25         #region PInvoke methods
26         
27         [DllImport("NtDll.dll", CharSet = CharSet.Unicode)]
28         [ResourceExposure(ResourceScope.Machine)]
29         internal static extern UInt32 NtCreateFile
30             (
31                 out Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle,
32                 Int32 desiredAccess,
33                 ref OBJECT_ATTRIBUTES objectAttributes,
34                 out IO_STATUS_BLOCK ioStatusBlock,
35                 ref Int64 allocationSize,
36                 UInt32 fileAttributes,
37                 System.IO.FileShare shareAccess,
38                 UInt32 createDisposition,
39                 UInt32 createOptions,
40                 SafeHandle eaBuffer,
41                 UInt32 eaLength
42             );
43
44         [DllImport("Kernel32.dll", SetLastError = true)]
45         [ResourceExposure(ResourceScope.None)]
46         internal static extern FileType GetFileType
47             (
48                 Microsoft.Win32.SafeHandles.SafeFileHandle hFile
49             );
50
51         // do not use this PInvoke directly, use SafeGetFullPathName instead
52         [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
53         [ResourceExposure(ResourceScope.Machine)]
54         private static extern int GetFullPathName
55             (
56                 string path, 
57                 int numBufferChars,
58                 StringBuilder buffer,
59                 IntPtr lpFilePartOrNull
60             );
61
62         /// <summary>
63         /// safe wrapper for GetFullPathName
64         /// check that the path length is less than Int16.MaxValue before calling this API!
65         /// </summary>
66         [ResourceExposure(ResourceScope.Machine)]
67         [ResourceConsumption(ResourceScope.Machine)]
68         internal static string SafeGetFullPathName(string path)
69         {
70             Debug.Assert(path != null, "path is null?");
71             // make sure to test for Int16.MaxValue limit before calling this method
72             // see the below comment re GetLastWin32Error for the reason
73             Debug.Assert(path.Length < Int16.MaxValue);
74
75             // since we expect network paths, the 'full path' is expected to be the same size
76             // as the provided one. we still need to allocate +1 for null termination
77             StringBuilder buffer = new StringBuilder(path.Length + 1);
78
79             int cchRequiredSize = GetFullPathName(path, buffer.Capacity, buffer, IntPtr.Zero);
80
81             // if our buffer was smaller than required, GetFullPathName will succeed and return us the required buffer size with null
82             if (cchRequiredSize > buffer.Capacity)
83             {
84                 // we have to reallocate and retry
85                 buffer.Capacity = cchRequiredSize;
86                 cchRequiredSize = GetFullPathName(path, buffer.Capacity, buffer, IntPtr.Zero);
87             }
88
89             if (cchRequiredSize == 0)
90             {
91                 // GetFullPathName call failed 
92                 int lastError = Marshal.GetLastWin32Error();
93                 if (lastError == 0)
94                 {
95                     // we found that in some cases GetFullPathName fail but does not set the last error value
96                     // for example, it happens when the path provided to it is longer than 32K: return value is 0 (failure)
97                     // but GetLastError was zero too so we raised Win32Exception saying "The operation completed successfully".
98                     // To raise proper "path too long" failure, check the length before calling this API.
99                     // For other (yet unknown cases), we will throw InvalidPath message since we do not know what exactly happened
100                     throw ADP.Argument(Res.GetString(Res.SqlFileStream_InvalidPath), "path");
101                 }
102                 else
103                 {
104                     System.ComponentModel.Win32Exception e = new System.ComponentModel.Win32Exception(lastError);
105                     ADP.TraceExceptionAsReturnValue(e);
106                     throw e;
107                 }
108             }
109
110             // this should not happen since we already reallocate
111             Debug.Assert(cchRequiredSize <= buffer.Capacity, string.Format(
112                 System.Globalization.CultureInfo.InvariantCulture,
113                 "second call to GetFullPathName returned greater size: {0} > {1}", 
114                 cchRequiredSize, 
115                 buffer.Capacity));
116
117             return buffer.ToString();
118         }
119
120         // RTM versions of Win7 and Windows Server 2008 R2
121         private static readonly Version ThreadErrorModeMinOsVersion = new Version(6, 1, 7600);
122
123         // do not use this method directly, use SetErrorModeWrapper instead
124         [DllImport("Kernel32.dll", ExactSpelling = true)]
125         [ResourceExposure(ResourceScope.Process)]
126         private static extern uint SetErrorMode(uint mode);
127
128         // do not use this method directly, use SetErrorModeWrapper instead
129         // this API exists since Windows 7 / Windows Server 2008 R2
130         [DllImport("Kernel32.dll", ExactSpelling = true, SetLastError = true)]
131         [ResourceExposure(ResourceScope.None)]
132         [SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")]
133         private static extern bool SetThreadErrorMode(uint newMode, out uint oldMode);
134
135         /// <summary>
136         /// this method uses thread-safe version of SetErrorMode on Windows 7/Windows Server 2008 R2 operating systems.
137         /// </summary>
138         [ResourceExposure(ResourceScope.Process)] // None on Windows7 / Windows Server 2008 R2 or later
139         [ResourceConsumption(ResourceScope.Process)]
140         internal static void SetErrorModeWrapper(uint mode, out uint oldMode)
141         {
142             if (Environment.OSVersion.Version >= ThreadErrorModeMinOsVersion)
143             {
144                 // safe to use new API
145                 if (!SetThreadErrorMode(mode, out oldMode))
146                 {
147                     throw new System.ComponentModel.Win32Exception();
148                 }
149             }
150             else
151             {
152                 // cannot use the new SetThreadErrorMode API on current OS, fallback to the old one
153                 oldMode = SetErrorMode(mode);
154             }
155         }
156
157         [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
158         [ResourceExposure(ResourceScope.Machine)]
159         internal static extern bool DeviceIoControl
160             (
161                 Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle,
162                 uint ioControlCode,
163                 IntPtr inBuffer,
164                 uint cbInBuffer,
165                 IntPtr outBuffer,
166                 uint cbOutBuffer,
167                 out uint cbBytesReturned,
168                 IntPtr overlapped
169             );
170
171         [DllImport("NtDll.dll")]
172         [ResourceExposure(ResourceScope.None)]
173         internal static extern UInt32 RtlNtStatusToDosError
174             (
175                 UInt32 status
176             );
177
178         #region definitions from devioctl.h
179
180         internal const ushort FILE_DEVICE_FILE_SYSTEM = 0x0009;
181         
182         internal enum Method
183         {
184             METHOD_BUFFERED,
185             METHOD_IN_DIRECT,
186             METHOD_OUT_DIRECT,
187             METHOD_NEITHER
188         };
189
190         internal enum Access
191         {
192             FILE_ANY_ACCESS,
193             FILE_READ_ACCESS,
194             FILE_WRITE_ACCESS
195         }
196
197         internal static uint CTL_CODE
198             (
199                 ushort deviceType,
200                 ushort function,
201                 byte method,
202                 byte access
203             )
204         {
205             if ( function > 4095 )
206                 throw ADP.ArgumentOutOfRange ( "function" );
207
208             return (uint) ( ( deviceType << 16 ) | ( access << 14 ) | ( function << 2 ) | method );
209         }
210
211         #endregion
212         
213         #endregion
214         
215         #region Error codes
216         
217         internal const int ERROR_INVALID_HANDLE             = 6;
218         internal const int ERROR_MR_MID_NOT_FOUND           = 317;
219         
220         internal const uint STATUS_INVALID_PARAMETER        = 0xc000000d;
221         internal const uint STATUS_SHARING_VIOLATION        = 0xc0000043;
222         internal const uint STATUS_OBJECT_NAME_NOT_FOUND    = 0xc0000034;
223
224         #endregion
225         
226         internal const uint SEM_FAILCRITICALERRORS = 0x0001;
227         
228         internal enum FileType : uint
229         {
230             Unknown = 0x0000,   // FILE_TYPE_UNKNOWN
231             Disk    = 0x0001,   // FILE_TYPE_DISK
232             Char    = 0x0002,   // FILE_TYPE_CHAR
233             Pipe    = 0x0003,   // FILE_TYPE_PIPE
234             Remote  = 0x8000    // FILE_TYPE_REMOTE
235         }
236
237         #region definitions from wdm.h
238
239         [StructLayoutAttribute(LayoutKind.Sequential)]
240         internal struct OBJECT_ATTRIBUTES
241         {
242             internal int length;
243             internal IntPtr rootDirectory;
244             internal SafeHandle objectName;
245             internal int attributes;
246             internal IntPtr securityDescriptor;
247             internal SafeHandle securityQualityOfService;
248         }
249
250         [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
251         internal struct UNICODE_STRING
252         {
253             internal UInt16 length;
254             internal UInt16 maximumLength;
255             internal string buffer;
256         }
257
258         // VSTFDevDiv # 547461 [Backport SqlFileStream fix on Win7 to QFE branch]
259         // Win7 enforces correct values for the _SECURITY_QUALITY_OF_SERVICE.qos member.
260         // taken from _SECURITY_IMPERSONATION_LEVEL enum definition in winnt.h
261         internal enum SecurityImpersonationLevel
262         {
263             SecurityAnonymous,
264             SecurityIdentification,
265             SecurityImpersonation,
266             SecurityDelegation
267         }
268
269         [StructLayoutAttribute(LayoutKind.Sequential)]
270         internal struct SECURITY_QUALITY_OF_SERVICE
271         {
272             internal UInt32 length;
273             [MarshalAs(UnmanagedType.I4)]
274             internal int impersonationLevel;
275             internal byte contextDynamicTrackingMode;
276             internal byte effectiveOnly;
277         }
278
279         [StructLayoutAttribute(LayoutKind.Sequential)]
280         internal struct IO_STATUS_BLOCK
281         {
282             internal UInt32 status;
283             internal IntPtr information;
284         }
285
286         [StructLayoutAttribute(LayoutKind.Sequential)]
287         internal struct FILE_FULL_EA_INFORMATION
288         {
289             internal UInt32 nextEntryOffset;
290             internal Byte flags;
291             internal Byte EaNameLength;
292             internal UInt16 EaValueLength;
293             internal Byte EaName;
294         }
295
296         [Flags]
297         internal enum CreateOption : uint
298         {
299             FILE_WRITE_THROUGH = 0x00000002,
300             FILE_SEQUENTIAL_ONLY = 0x00000004,
301             FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008,
302             FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020,
303             FILE_RANDOM_ACCESS = 0x00000800
304         }
305
306         internal enum CreationDisposition : uint
307         {
308             FILE_SUPERSEDE = 0,
309             FILE_OPEN = 1,
310             FILE_CREATE = 2,
311             FILE_OPEN_IF = 3,
312             FILE_OVERWRITE = 4,
313             FILE_OVERWRITE_IF = 5
314         }
315         
316         #endregion
317
318         #region definitions from winnt.h
319
320         internal const int FILE_READ_DATA       = 0x0001;
321         internal const int FILE_WRITE_DATA      = 0x0002;
322         internal const int FILE_READ_ATTRIBUTES = 0x0080;
323         internal const int SYNCHRONIZE          = 0x00100000;
324
325         #endregion
326
327         #region definitions from ntdef.h
328
329         [Flags]
330         internal enum Attributes : uint
331         {
332             Inherit             = 0x00000002,
333             Permanent           = 0x00000010,
334             Exclusive           = 0x00000020,
335             CaseInsensitive     = 0x00000040,
336             OpenIf              = 0x00000080,
337             OpenLink            = 0x00000100,
338             KernelHandle        = 0x00000200,
339             ForceAccessCheck    = 0x00000400,
340             ValidAttributes     = 0x000007F2
341         }
342
343         #endregion
344
345     }
346 }