Merge pull request #3626 from lateralusX/jlorenss/win-api-family-support-eglib
[mono.git] / mcs / class / System.Core / System.IO.MemoryMappedFiles / MemoryMappedFile.cs
1 //
2 // MemoryMappedFile.cs
3 //
4 // Authors:
5 //      Zoltan Varga (vargaz@gmail.com)
6 //
7 // Copyright (C) 2009, Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.IO;
31 using System.Collections.Generic;
32 using Microsoft.Win32.SafeHandles;
33 using System.Runtime.InteropServices;
34 using System.Runtime.CompilerServices;
35
36
37 namespace System.IO.MemoryMappedFiles
38 {
39         internal static class MemoryMapImpl {
40                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
41                 static extern IntPtr OpenFileInternal (string path, FileMode mode, string mapName, out long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, out int error);
42
43                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
44                 static extern IntPtr OpenHandleInternal (IntPtr handle, string mapName, out long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, out int error);
45
46                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
47                 internal extern static void CloseMapping (IntPtr handle);
48
49                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
50                 internal extern static void Flush (IntPtr file_handle);
51
52                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
53                 internal extern static void ConfigureHandleInheritability (IntPtr handle, HandleInheritability inheritability);
54
55                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
56                 internal extern static bool Unmap (IntPtr mmap_handle);
57
58                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
59                 extern static int MapInternal (IntPtr handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr mmap_handle, out IntPtr base_address);
60
61                 internal static void Map (IntPtr handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr mmap_handle, out IntPtr base_address)
62                 {
63                         int error = MapInternal (handle, offset, ref size, access, out mmap_handle, out base_address);
64                         if (error != 0)
65                                 throw CreateException (error, "<none>");
66                 }
67
68                 static Exception CreateException (int error, string path) {
69                         switch (error){
70                         case 1:
71                                 return new ArgumentException ("A positive capacity must be specified for a Memory Mapped File backed by an empty file.");
72                         case 2:
73                                 return new ArgumentOutOfRangeException ("The capacity may not be smaller than the file size.");
74                         case 3:
75                                 return new FileNotFoundException (path);
76                         case 4:
77                                 return new IOException ("The file already exists");
78                         case 5:
79                                 return new PathTooLongException ();
80                         case 6:
81                                 return new IOException ("Could not open file");
82                         case 7:
83                                 return new ArgumentException ("Capacity must be bigger than zero for non-file mappings");
84                         case 8:
85                                 return new ArgumentException ("Invalid FileMode value.");
86                         case 9:
87                                 return new IOException ("Could not map file");
88                         default:
89                                 return new IOException ("Failed with unknown error code " + error);
90                         }
91                 }
92
93                 internal static IntPtr OpenFile (string path, FileMode mode, string mapName, out long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options)
94                 {
95                         int error = 0;
96                         IntPtr res = OpenFileInternal (path, mode, mapName, out capacity, access, options, out error);
97                         if (error != 0)
98                                 throw CreateException (error, path);
99                         return res;
100                 }
101
102                 internal static IntPtr OpenHandle (IntPtr handle, string mapName, out long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options)
103                 {
104                         int error = 0;
105                         IntPtr res = OpenHandleInternal (handle, mapName, out capacity, access, options, out error);
106                         if (error != 0)
107                                 throw CreateException (error, "<none>");
108                         return res;
109                 }
110         }
111
112
113         public class MemoryMappedFile : IDisposable {
114                 // MemoryMappedFileAccess fileAccess;
115                 // string name;
116                 // long fileCapacity;
117
118                 //
119                 // We allow the use of either the FileStream/keepOpen combo
120                 // or a Unix file descriptor.  This way we avoid the dependency on
121                 // Mono's io-layer having the Unix file descriptors mapped to
122                 // the same io-layer handle
123                 //
124                 FileStream stream;
125                 bool keepOpen;
126                 IntPtr handle;
127
128                 public static MemoryMappedFile CreateFromFile (string path)
129                 {
130                         return CreateFromFile (path, FileMode.Open, null, 0, MemoryMappedFileAccess.ReadWrite);
131                 }
132
133                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode)
134                 {
135                         long capacity = 0;
136                         if (path == null)
137                                 throw new ArgumentNullException ("path");
138                         if (path.Length == 0)
139                                 throw new ArgumentException ("path");
140                         if (mode == FileMode.Append)
141                                 throw new ArgumentException ("mode");
142
143                         IntPtr handle = MemoryMapImpl.OpenFile (path, mode, null, out capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages);
144
145                         return new MemoryMappedFile () {
146                                 handle = handle,
147                                 // fileAccess = MemoryMappedFileAccess.ReadWrite,
148                                 // fileCapacity = capacity
149                         };
150                 }
151
152                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName)
153                 {
154                         return CreateFromFile (path, mode, mapName, 0, MemoryMappedFileAccess.ReadWrite);
155                 }
156
157                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity)
158                 {
159                         return CreateFromFile (path, mode, mapName, capacity, MemoryMappedFileAccess.ReadWrite);
160                 }
161
162                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity, MemoryMappedFileAccess access)
163                 {
164                         if (path == null)
165                                 throw new ArgumentNullException ("path");
166                         if (path.Length == 0)
167                                 throw new ArgumentException ("path");
168                         if (mapName != null && mapName.Length == 0)
169                                 throw new ArgumentException ("mapName");
170                         if (mode == FileMode.Append)
171                                 throw new ArgumentException ("mode");                   
172                         if (capacity < 0)
173                                 throw new ArgumentOutOfRangeException ("capacity");
174
175                         IntPtr handle = MemoryMapImpl.OpenFile (path, mode, mapName, out capacity, access, MemoryMappedFileOptions.DelayAllocatePages);
176                         
177                         return new MemoryMappedFile () {
178                                 handle = handle,
179                                 // fileAccess = access,
180                                 // name = mapName,
181                                 // fileCapacity = capacity
182                         };
183                 }
184
185                 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
186                                                                HandleInheritability inheritability,
187                                                                bool leaveOpen)
188                 {
189                         if (fileStream == null)
190                                 throw new ArgumentNullException ("fileStream");
191                         if (mapName != null && mapName.Length == 0)
192                                 throw new ArgumentException ("mapName");
193                         if ((!MonoUtil.IsUnix && capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
194                                 throw new ArgumentException ("capacity");
195
196                         IntPtr handle = MemoryMapImpl.OpenHandle (fileStream.SafeFileHandle.DangerousGetHandle (), mapName, out capacity, access, MemoryMappedFileOptions.DelayAllocatePages);
197                         
198                         MemoryMapImpl.ConfigureHandleInheritability (handle, inheritability);
199                                 
200                         return new MemoryMappedFile () {
201                                 handle = handle,
202                                 // fileAccess = access,
203                                 // name = mapName,
204                                 // fileCapacity = capacity,
205
206                                 stream = fileStream,
207                                 keepOpen = leaveOpen
208                         };
209                 }
210
211                 [MonoLimitation ("memoryMappedFileSecurity is currently ignored")]
212                 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
213                                                                MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability,
214                                                                bool leaveOpen)
215                 {
216                         if (fileStream == null)
217                                 throw new ArgumentNullException ("fileStream");
218                         if (mapName != null && mapName.Length == 0)
219                                 throw new ArgumentException ("mapName");
220                         if ((!MonoUtil.IsUnix && capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
221                                 throw new ArgumentException ("capacity");
222
223                         IntPtr handle = MemoryMapImpl.OpenHandle (fileStream.SafeFileHandle.DangerousGetHandle (), mapName, out capacity, access, MemoryMappedFileOptions.DelayAllocatePages);
224                         
225                         MemoryMapImpl.ConfigureHandleInheritability (handle, inheritability);
226                                 
227                         return new MemoryMappedFile () {
228                                 handle = handle,
229                                 // fileAccess = access,
230                                 // name = mapName,
231                                 // fileCapacity = capacity,
232
233                                 stream = fileStream,
234                                 keepOpen = leaveOpen
235                         };
236                 }
237
238
239                 static MemoryMappedFile CoreShmCreate (string mapName, long capacity, MemoryMappedFileAccess access,
240                                                           MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity,
241                                                           HandleInheritability inheritability, FileMode mode)
242                 {
243                         if (mapName != null && mapName.Length == 0)
244                                 throw new ArgumentException ("mapName");
245                         if (capacity < 0)
246                                 throw new ArgumentOutOfRangeException ("capacity");
247
248                         IntPtr handle = MemoryMapImpl.OpenFile (null, mode, mapName, out capacity, access, options);
249                         
250                         return new MemoryMappedFile () {
251                                 handle = handle,
252                                 // fileAccess = access,
253                                 // name = mapName,
254                                 // fileCapacity = capacity
255                         };                      
256                 }
257
258                 [MonoLimitation ("Named mappings scope is process local")]
259                 public static MemoryMappedFile CreateNew (string mapName, long capacity)
260                 {
261                         return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, null, HandleInheritability.None);
262                 }
263
264                 [MonoLimitation ("Named mappings scope is process local")]
265                 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access) 
266                 {
267                         return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, null, HandleInheritability.None);
268                 }
269
270                 [MonoLimitation ("Named mappings scope is process local; options is ignored")]
271                 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, HandleInheritability inheritability)
272                 {
273                         return CreateNew (mapName, capacity, access, options, null, inheritability);
274                 }
275
276                 [MonoLimitation ("Named mappings scope is process local; options and memoryMappedFileSecurity are ignored")]
277                 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access,
278                                                           MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity,
279                                                           HandleInheritability inheritability)
280                 {
281                         return CoreShmCreate (mapName, capacity, access, options, memoryMappedFileSecurity, inheritability, FileMode.CreateNew);
282                 }
283
284                 [MonoLimitation ("Named mappings scope is process local")]
285                 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity)
286                 {
287                         return CreateOrOpen (mapName, capacity, MemoryMappedFileAccess.ReadWrite);
288                 }
289
290                 [MonoLimitation ("Named mappings scope is process local")]
291                 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access)
292                 {
293                         return CreateOrOpen (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, null, HandleInheritability.None);
294                 }
295
296                 [MonoLimitation ("Named mappings scope is process local")]
297                 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, HandleInheritability inheritability)
298                 {
299                         return CreateOrOpen (mapName, capacity, access, options, null, inheritability);
300                 }
301
302                 [MonoLimitation ("Named mappings scope is process local")]
303                 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability)
304                 {
305                         return CoreShmCreate (mapName, capacity, access, options, memoryMappedFileSecurity, inheritability, FileMode.OpenOrCreate);
306                 }
307
308                 [MonoLimitation ("Named mappings scope is process local")]
309                 public static MemoryMappedFile OpenExisting (string mapName)
310                 {
311                         throw new NotImplementedException ();
312                 }
313
314                 [MonoLimitation ("Named mappings scope is process local")]
315                 public static MemoryMappedFile OpenExisting (string mapName, MemoryMappedFileRights desiredAccessRights)
316                 {
317                         throw new NotImplementedException ();
318                 }
319
320                 [MonoLimitation ("Named mappings scope is process local")]
321                 public static MemoryMappedFile OpenExisting (string mapName, MemoryMappedFileRights desiredAccessRights, HandleInheritability inheritability)
322                 {
323                         throw new NotImplementedException ();
324                 }
325
326                 public MemoryMappedViewStream CreateViewStream ()
327                 {
328                         return CreateViewStream (0, 0);//FIXME this is wrong
329                 }
330
331                 public MemoryMappedViewStream CreateViewStream (long offset, long size)
332                 {
333                         return CreateViewStream (offset, size, MemoryMappedFileAccess.ReadWrite);
334                 }
335
336                 public MemoryMappedViewStream CreateViewStream (long offset, long size, MemoryMappedFileAccess access)
337                 {
338                         var view = MemoryMappedView.Create (handle, offset, size, access);
339                         return new MemoryMappedViewStream (view);
340                 }
341
342                 public MemoryMappedViewAccessor CreateViewAccessor ()
343                 {
344                         return CreateViewAccessor (0, 0);
345                 }
346
347                 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size)
348                 {
349                         return CreateViewAccessor (offset, size, MemoryMappedFileAccess.ReadWrite);
350                 }
351
352                 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size, MemoryMappedFileAccess access)
353                 {
354                         var view = MemoryMappedView.Create (handle, offset, size, access);
355                         return new MemoryMappedViewAccessor (view);
356                 }
357
358                 MemoryMappedFile ()
359                 {
360                 }
361
362                 public void Dispose ()
363                 {
364                         Dispose (true);
365                 }
366
367                 protected virtual void Dispose (bool disposing)
368                 {
369                         if (disposing){
370                                 if (stream != null){
371                                         if (keepOpen == false)
372                                                 stream.Close ();
373                                         stream = null;
374                                 }
375                                 if (handle != IntPtr.Zero) {
376                                         MemoryMapImpl.CloseMapping (handle);
377                                         handle = IntPtr.Zero;
378                                 }
379                         }
380                 }
381
382                 [MonoTODO]
383                 public MemoryMappedFileSecurity GetAccessControl ()
384                 {
385                         throw new NotImplementedException ();
386                 }
387
388                 [MonoTODO]
389                 public void SetAccessControl (MemoryMappedFileSecurity memoryMappedFileSecurity)
390                 {
391                         throw new NotImplementedException ();
392                 }
393
394                 [MonoTODO]
395                 public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle {
396                         get {
397                                 throw new NotImplementedException ();
398                         }
399                 }
400
401                 // This converts a MemoryMappedFileAccess to a FileAccess. MemoryMappedViewStream and
402                 // MemoryMappedViewAccessor subclass UnmanagedMemoryStream and UnmanagedMemoryAccessor, which both use
403                 // FileAccess to determine whether they are writable and/or readable.
404                 internal static FileAccess GetFileAccess (MemoryMappedFileAccess access) {
405
406                         if (access == MemoryMappedFileAccess.Read) {
407                                 return FileAccess.Read;
408                         }
409                         if (access == MemoryMappedFileAccess.Write) {
410                                 return FileAccess.Write;
411                         }
412                         else if (access == MemoryMappedFileAccess.ReadWrite) {
413                                 return FileAccess.ReadWrite;
414                         }
415                         else if (access == MemoryMappedFileAccess.CopyOnWrite) {
416                                 return FileAccess.ReadWrite;
417                         }
418                         else if (access == MemoryMappedFileAccess.ReadExecute) {
419                                 return FileAccess.Read;
420                         }
421                         else if (access == MemoryMappedFileAccess.ReadWriteExecute) {
422                                 return FileAccess.ReadWrite;
423                         }
424
425                         // If we reached here, access was invalid.
426                         throw new ArgumentOutOfRangeException ("access");
427                 }
428         }
429 }
430