Merge pull request #4540 from kumpera/android-changes-part1
[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 namespace System.IO.MemoryMappedFiles
37 {
38         internal static class MemoryMapImpl {
39                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
40                 static extern IntPtr OpenFileInternal (string path, FileMode mode, string mapName, out long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, out int error);
41
42                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
43                 static extern IntPtr OpenHandleInternal (IntPtr handle, string mapName, out long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, out int error);
44
45                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
46                 internal extern static void CloseMapping (IntPtr handle);
47
48                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
49                 internal extern static void Flush (IntPtr file_handle);
50
51                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
52                 internal extern static void ConfigureHandleInheritability (IntPtr handle, HandleInheritability inheritability);
53
54                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
55                 internal extern static bool Unmap (IntPtr mmap_handle);
56
57                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
58                 extern static int MapInternal (IntPtr handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr mmap_handle, out IntPtr base_address);
59
60                 internal static void Map (IntPtr handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr mmap_handle, out IntPtr base_address)
61                 {
62                         int error = MapInternal (handle, offset, ref size, access, out mmap_handle, out base_address);
63                         if (error != 0)
64                                 throw CreateException (error, "<none>");
65                 }
66
67                 static Exception CreateException (int error, string path) {
68                         switch (error){
69                         case 1:
70                                 return new ArgumentException ("A positive capacity must be specified for a Memory Mapped File backed by an empty file.");
71                         case 2:
72                                 return new ArgumentOutOfRangeException ("capacity", "The capacity may not be smaller than the file size.");
73                         case 3:
74                                 return new FileNotFoundException (path);
75                         case 4:
76                                 return new IOException ("The file already exists");
77                         case 5:
78                                 return new PathTooLongException ();
79                         case 6:
80                                 return new IOException ("Could not open file");
81                         case 7:
82                                 return new ArgumentException ("Capacity must be bigger than zero for non-file mappings");
83                         case 8:
84                                 return new ArgumentException ("Invalid FileMode value.");
85                         case 9:
86                                 return new IOException ("Could not map file");
87                         case 10:
88                                 return new UnauthorizedAccessException ("Access to the path is denied.");
89                         case 11:
90                                 return new ArgumentOutOfRangeException ("capacity", "The capacity cannot be greater than the size of the system's logical address space.");
91                         default:
92                                 return new IOException ("Failed with unknown error code " + error);
93                         }
94                 }
95
96                 internal static IntPtr OpenFile (string path, FileMode mode, string mapName, out long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options)
97                 {
98                         int error = 0;
99                         IntPtr res = OpenFileInternal (path, mode, mapName, out capacity, access, options, out error);
100                         if (error != 0)
101                                 throw CreateException (error, path);
102                         return res;
103                 }
104
105                 internal static IntPtr OpenHandle (IntPtr handle, string mapName, out long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options)
106                 {
107                         int error = 0;
108                         IntPtr res = OpenHandleInternal (handle, mapName, out capacity, access, options, out error);
109                         if (error != 0)
110                                 throw CreateException (error, "<none>");
111                         return res;
112                 }
113         }
114
115         public class MemoryMappedFile : IDisposable {
116                 // MemoryMappedFileAccess fileAccess;
117                 // string name;
118                 // long fileCapacity;
119
120                 //
121                 // We allow the use of either the FileStream/keepOpen combo
122                 // or a Unix file descriptor.  This way we avoid the dependency on
123                 // Mono's io-layer having the Unix file descriptors mapped to
124                 // the same io-layer handle
125                 //
126                 FileStream stream;
127                 bool keepOpen;
128                 SafeMemoryMappedFileHandle handle;
129
130                 public static MemoryMappedFile CreateFromFile (string path)
131                 {
132                         return CreateFromFile (path, FileMode.Open, null, 0, MemoryMappedFileAccess.ReadWrite);
133                 }
134
135                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode)
136                 {
137                         long capacity = 0;
138                         if (path == null)
139                                 throw new ArgumentNullException ("path");
140                         if (path.Length == 0)
141                                 throw new ArgumentException ("path");
142                         if (mode == FileMode.Append)
143                                 throw new ArgumentException ("mode");
144
145                         IntPtr handle = MemoryMapImpl.OpenFile (path, mode, null, out capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None);
146
147                         return new MemoryMappedFile () {
148                                 handle = new SafeMemoryMappedFileHandle (handle, true),
149                                 // fileAccess = MemoryMappedFileAccess.ReadWrite,
150                                 // fileCapacity = capacity
151                         };
152                 }
153
154                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName)
155                 {
156                         return CreateFromFile (path, mode, mapName, 0, MemoryMappedFileAccess.ReadWrite);
157                 }
158
159                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity)
160                 {
161                         return CreateFromFile (path, mode, mapName, capacity, MemoryMappedFileAccess.ReadWrite);
162                 }
163
164                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity, MemoryMappedFileAccess access)
165                 {
166                         if (path == null)
167                                 throw new ArgumentNullException ("path");
168                         if (path.Length == 0)
169                                 throw new ArgumentException ("path");
170                         if (mapName != null && mapName.Length == 0)
171                                 throw new ArgumentException ("mapName");
172                         if (mode == FileMode.Append)
173                                 throw new ArgumentException ("mode");                   
174                         if (capacity < 0)
175                                 throw new ArgumentOutOfRangeException ("capacity");
176
177                         IntPtr handle = MemoryMapImpl.OpenFile (path, mode, mapName, out capacity, access, MemoryMappedFileOptions.None);
178                         
179                         return new MemoryMappedFile () {
180                                 handle = new SafeMemoryMappedFileHandle (handle, true),
181                                 // fileAccess = access,
182                                 // name = mapName,
183                                 // fileCapacity = capacity
184                         };
185                 }
186
187                 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
188                                                                HandleInheritability inheritability,
189                                                                bool leaveOpen)
190                 {
191                         if (fileStream == null)
192                                 throw new ArgumentNullException ("fileStream");
193                         if (mapName != null && mapName.Length == 0)
194                                 throw new ArgumentException ("mapName");
195                         if ((!MonoUtil.IsUnix && capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
196                                 throw new ArgumentException ("capacity");
197
198                         IntPtr handle = MemoryMapImpl.OpenHandle (fileStream.SafeFileHandle.DangerousGetHandle (), mapName, out capacity, access, MemoryMappedFileOptions.None);
199                         
200                         MemoryMapImpl.ConfigureHandleInheritability (handle, inheritability);
201                                 
202                         return new MemoryMappedFile () {
203                                 handle = new SafeMemoryMappedFileHandle (handle, true),
204                                 // fileAccess = access,
205                                 // name = mapName,
206                                 // fileCapacity = capacity,
207
208                                 stream = fileStream,
209                                 keepOpen = leaveOpen
210                         };
211                 }
212
213                 [MonoLimitation ("memoryMappedFileSecurity is currently ignored")]
214                 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
215                                                                MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability,
216                                                                bool leaveOpen)
217                 {
218                         if (fileStream == null)
219                                 throw new ArgumentNullException ("fileStream");
220                         if (mapName != null && mapName.Length == 0)
221                                 throw new ArgumentException ("mapName");
222                         if ((!MonoUtil.IsUnix && capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
223                                 throw new ArgumentException ("capacity");
224
225                         IntPtr handle = MemoryMapImpl.OpenHandle (fileStream.SafeFileHandle.DangerousGetHandle (), mapName, out capacity, access, MemoryMappedFileOptions.None);
226                         
227                         MemoryMapImpl.ConfigureHandleInheritability (handle, inheritability);
228                                 
229                         return new MemoryMappedFile () {
230                                 handle = new SafeMemoryMappedFileHandle (handle, true),
231                                 // fileAccess = access,
232                                 // name = mapName,
233                                 // fileCapacity = capacity,
234
235                                 stream = fileStream,
236                                 keepOpen = leaveOpen
237                         };
238                 }
239
240
241                 static MemoryMappedFile CoreShmCreate (string mapName, long capacity, MemoryMappedFileAccess access,
242                                                           MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity,
243                                                           HandleInheritability inheritability, FileMode mode)
244                 {
245                         if (mapName != null && mapName.Length == 0)
246                                 throw new ArgumentException ("mapName");
247                         if (capacity < 0)
248                                 throw new ArgumentOutOfRangeException ("capacity");
249
250                         IntPtr handle = MemoryMapImpl.OpenFile (null, mode, mapName, out capacity, access, options);
251                         
252                         return new MemoryMappedFile () {
253                                 handle = new SafeMemoryMappedFileHandle (handle, true),
254                                 // fileAccess = access,
255                                 // name = mapName,
256                                 // fileCapacity = capacity
257                         };                      
258                 }
259
260                 [MonoLimitation ("Named mappings scope is process local")]
261                 public static MemoryMappedFile CreateNew (string mapName, long capacity)
262                 {
263                         return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None, null, HandleInheritability.None);
264                 }
265
266                 [MonoLimitation ("Named mappings scope is process local")]
267                 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access) 
268                 {
269                         return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.None, null, HandleInheritability.None);
270                 }
271
272                 [MonoLimitation ("Named mappings scope is process local; options is ignored")]
273                 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, HandleInheritability inheritability)
274                 {
275                         return CreateNew (mapName, capacity, access, options, null, inheritability);
276                 }
277
278                 [MonoLimitation ("Named mappings scope is process local; options and memoryMappedFileSecurity are ignored")]
279                 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access,
280                                                           MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity,
281                                                           HandleInheritability inheritability)
282                 {
283                         return CoreShmCreate (mapName, capacity, access, options, memoryMappedFileSecurity, inheritability, FileMode.CreateNew);
284                 }
285
286                 [MonoLimitation ("Named mappings scope is process local")]
287                 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity)
288                 {
289                         return CreateOrOpen (mapName, capacity, MemoryMappedFileAccess.ReadWrite);
290                 }
291
292                 [MonoLimitation ("Named mappings scope is process local")]
293                 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access)
294                 {
295                         return CreateOrOpen (mapName, capacity, access, MemoryMappedFileOptions.None, null, HandleInheritability.None);
296                 }
297
298                 [MonoLimitation ("Named mappings scope is process local")]
299                 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, HandleInheritability inheritability)
300                 {
301                         return CreateOrOpen (mapName, capacity, access, options, null, inheritability);
302                 }
303
304                 [MonoLimitation ("Named mappings scope is process local")]
305                 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability)
306                 {
307                         return CoreShmCreate (mapName, capacity, access, options, memoryMappedFileSecurity, inheritability, FileMode.OpenOrCreate);
308                 }
309
310                 [MonoLimitation ("Named mappings scope is process local")]
311                 public static MemoryMappedFile OpenExisting (string mapName)
312                 {
313                         throw new NotImplementedException ();
314                 }
315
316                 [MonoLimitation ("Named mappings scope is process local")]
317                 public static MemoryMappedFile OpenExisting (string mapName, MemoryMappedFileRights desiredAccessRights)
318                 {
319                         throw new NotImplementedException ();
320                 }
321
322                 [MonoLimitation ("Named mappings scope is process local")]
323                 public static MemoryMappedFile OpenExisting (string mapName, MemoryMappedFileRights desiredAccessRights, HandleInheritability inheritability)
324                 {
325                         throw new NotImplementedException ();
326                 }
327
328                 public MemoryMappedViewStream CreateViewStream ()
329                 {
330                         return CreateViewStream (0, 0);//FIXME this is wrong
331                 }
332
333                 public MemoryMappedViewStream CreateViewStream (long offset, long size)
334                 {
335                         return CreateViewStream (offset, size, MemoryMappedFileAccess.ReadWrite);
336                 }
337
338                 public MemoryMappedViewStream CreateViewStream (long offset, long size, MemoryMappedFileAccess access)
339                 {
340                         var view = MemoryMappedView.Create (handle.DangerousGetHandle (), offset, size, access);
341                         return new MemoryMappedViewStream (view);
342                 }
343
344                 public MemoryMappedViewAccessor CreateViewAccessor ()
345                 {
346                         return CreateViewAccessor (0, 0);
347                 }
348
349                 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size)
350                 {
351                         return CreateViewAccessor (offset, size, MemoryMappedFileAccess.ReadWrite);
352                 }
353
354                 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size, MemoryMappedFileAccess access)
355                 {
356                         var view = MemoryMappedView.Create (handle.DangerousGetHandle (), offset, size, access);
357                         return new MemoryMappedViewAccessor (view);
358                 }
359
360                 MemoryMappedFile ()
361                 {
362                 }
363
364                 public void Dispose ()
365                 {
366                         Dispose (true);
367                 }
368
369                 protected virtual void Dispose (bool disposing)
370                 {
371                         if (disposing) {
372                                 if (stream != null) {
373                                         if (keepOpen == false)
374                                                 stream.Close ();
375                                         stream = null;
376                                 }
377                         }
378                         if (handle != null) {
379                                 handle.Dispose ();
380                                 handle = null;
381                         }
382                 }
383
384                 [MonoTODO]
385                 public MemoryMappedFileSecurity GetAccessControl ()
386                 {
387                         throw new NotImplementedException ();
388                 }
389
390                 [MonoTODO]
391                 public void SetAccessControl (MemoryMappedFileSecurity memoryMappedFileSecurity)
392                 {
393                         throw new NotImplementedException ();
394                 }
395
396                 [MonoTODO]
397                 public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle {
398                         get {
399                                 throw new NotImplementedException ();
400                         }
401                 }
402
403                 // This converts a MemoryMappedFileAccess to a FileAccess. MemoryMappedViewStream and
404                 // MemoryMappedViewAccessor subclass UnmanagedMemoryStream and UnmanagedMemoryAccessor, which both use
405                 // FileAccess to determine whether they are writable and/or readable.
406                 internal static FileAccess GetFileAccess (MemoryMappedFileAccess access) {
407
408                         if (access == MemoryMappedFileAccess.Read) {
409                                 return FileAccess.Read;
410                         }
411                         if (access == MemoryMappedFileAccess.Write) {
412                                 return FileAccess.Write;
413                         }
414                         else if (access == MemoryMappedFileAccess.ReadWrite) {
415                                 return FileAccess.ReadWrite;
416                         }
417                         else if (access == MemoryMappedFileAccess.CopyOnWrite) {
418                                 return FileAccess.ReadWrite;
419                         }
420                         else if (access == MemoryMappedFileAccess.ReadExecute) {
421                                 return FileAccess.Read;
422                         }
423                         else if (access == MemoryMappedFileAccess.ReadWriteExecute) {
424                                 return FileAccess.ReadWrite;
425                         }
426
427                         // If we reached here, access was invalid.
428                         throw new ArgumentOutOfRangeException ("access");
429                 }
430         }
431 }
432