2009-12-13 Miguel de Icaza <miguel@novell.com>
[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 #if NET_4_0
30
31 using System;
32 using System.IO;
33 using System.Collections.Generic;
34 using Microsoft.Win32.SafeHandles;
35 using Mono.Unix.Native;
36 using System.Runtime.InteropServices;
37
38 namespace System.IO.MemoryMappedFiles
39 {
40         public class MemoryMappedFile : IDisposable {
41                 MemoryMappedFileAccess fileAccess;
42                 string name;
43                 FileStream stream;
44                 long fileCapacity;
45                 bool keepOpen;
46                 
47                 public static MemoryMappedFile CreateFromFile (FileStream fileStream) {
48                         if (fileStream == null)
49                                 throw new ArgumentNullException ("fileStream");
50
51                         return new MemoryMappedFile () {
52                                 stream = fileStream,
53                                 fileAccess = MemoryMappedFileAccess.ReadWrite
54                         };
55                 }
56
57                 public static MemoryMappedFile CreateFromFile (string path)
58                 {
59                         return CreateFromFile (path, FileMode.Open, null, 0, MemoryMappedFileAccess.ReadWrite);
60                 }
61
62
63                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode)
64                 {
65                         return CreateFromFile (path, mode, null, 0, MemoryMappedFileAccess.ReadWrite);
66                 }
67
68                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName)
69                 {
70                         return CreateFromFile (path, mode, mapName, 0, MemoryMappedFileAccess.ReadWrite);
71                 }
72
73                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity)
74                 {
75                         return CreateFromFile (path, mode, mapName, capacity, MemoryMappedFileAccess.ReadWrite);
76                 }
77
78                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity, MemoryMappedFileAccess access)
79                 {
80                         if (path == null)
81                                 throw new ArgumentNullException ("path");
82                         if (path.Length == 0)
83                                 throw new ArgumentException ("path");
84                         if (mapName != null && mapName.Length == 0)
85                                 throw new ArgumentException ("mapName");
86                         var fileStream = File.Open (path, mode);
87
88                         if ((capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length)){
89                                 fileStream.Close ();
90                                 throw new ArgumentException ("capacity");
91                         }
92                         return new MemoryMappedFile () {
93                                 stream = fileStream,
94                                 fileAccess = access,
95                                 name = mapName,
96                                 fileCapacity = capacity
97                                         };
98                 }
99
100                 public static void ConfigureUnixFD (IntPtr handle, HandleInheritability h)
101                 {
102                         // TODO: Mono.Posix is lacking O_CLOEXEC definitions for fcntl.
103                 }
104
105
106                 [DllImport("kernel32.dll", SetLastError = true)]
107                 static extern bool SetHandleInformation (IntPtr hObject, int dwMask, int dwFlags);
108                 public static void ConfigureWindowsFD (IntPtr handle, HandleInheritability h)
109                 {
110                         SetHandleInformation (handle, 1 /* FLAG_INHERIT */, h == HandleInheritability.None ? 0 : 1);
111                 }
112
113                 [MonoLimitation ("memoryMappedFileSecurity is currently ignored")]
114                 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
115                                                                MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability,
116                                                                bool leaveOpen)
117                 {
118                         if (fileStream == null)
119                                 throw new ArgumentNullException ("fileStream");
120                         if (mapName != null && mapName.Length == 0)
121                                 throw new ArgumentException ("mapName");
122                         if ((capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
123                                 throw new ArgumentException ("capacity");
124
125                         if (MonoUtil.IsUnix)
126                                 ConfigureUnixFD (fileStream.Handle, inheritability);
127                         else
128                                 ConfigureWindowsFD (fileStream.Handle, inheritability);
129                                 
130                         return new MemoryMappedFile () {
131                                 stream = fileStream,
132                                 fileAccess = access,
133                                 name = mapName,
134                                 fileCapacity = capacity,
135                                 keepOpen = leaveOpen
136                         };
137                 }
138
139
140                 [MonoTODO]
141                 public static MemoryMappedFile CreateNew (string mapName, long capacity)
142                 {
143                         throw new NotImplementedException ();
144                 }
145
146                 [MonoTODO]
147                 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access) 
148                 {
149                         throw new NotImplementedException ();
150                 }
151
152                 /*
153                 [MonoTODO]
154                         public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability handleInheritability) {
155                         throw new NotImplementedException ();
156                 }
157                 */
158
159                 [MonoTODO]
160                         public static MemoryMappedFile CreateOrOpen (string mapName, long capacity) {
161                         throw new NotImplementedException ();
162                 }
163
164                 [MonoTODO]
165                         public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access) {
166                         throw new NotImplementedException ();
167                 }
168
169                 /*
170                 [MonoTODO]
171                         public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability handleInheritability) {
172                         throw new NotImplementedException ();
173                 }
174                 */
175
176                 public MemoryMappedViewStream CreateViewStream ()
177                 {
178                         return CreateViewStream (0, 0);
179                 }
180
181                 public MemoryMappedViewStream CreateViewStream (long offset, long size)
182                 {
183                         return CreateViewStream (offset, size, MemoryMappedFileAccess.ReadWrite);
184                 }
185
186                 [MonoTODO]
187                 public MemoryMappedViewStream CreateViewStream (long offset, long size, MemoryMappedFileAccess access)
188                 {
189                         return new MemoryMappedViewStream (stream, offset, size, access);
190                 }
191
192                 public MemoryMappedViewAccessor CreateViewAccessor ()
193                 {
194                         return CreateViewAccessor (0, 0);
195                 }
196
197                 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size)
198                 {
199                         return CreateViewAccessor (offset, size, MemoryMappedFileAccess.ReadWrite);
200                 }
201
202                 [MonoTODO]
203                 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size, MemoryMappedFileAccess access)
204                 {
205                         return new MemoryMappedViewAccessor (stream, offset, size, access);
206                 }
207
208                 MemoryMappedFile ()
209                 {
210                 }
211
212                 public void Dispose ()
213                 {
214                         Dispose (true);
215                 }
216
217                 protected virtual void Dispose (bool disposing)
218                 {
219                         if (disposing){
220                                 if (stream != null && keepOpen == false)
221                                         stream.Close ();
222                                 stream = null;
223                         }
224                 }
225                 
226                 [MonoTODO]
227                 public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle {
228                         get {
229                                 throw new NotImplementedException ();
230                         }
231                 }
232
233                 static int pagesize;
234
235                 static MmapProts ToUnixProts (MemoryMappedFileAccess access)
236                 {
237                         MmapProts prots;
238                         
239                         switch (access){
240                         case MemoryMappedFileAccess.ReadWrite:
241                                 return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
242                                 break;
243                                 
244                         case MemoryMappedFileAccess.Write:
245                                 return MmapProts.PROT_WRITE;
246                                 
247                         case MemoryMappedFileAccess.CopyOnWrite:
248                                 return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
249                                 
250                         case MemoryMappedFileAccess.ReadExecute:
251                                 return MmapProts.PROT_EXEC;
252                                 
253                         case MemoryMappedFileAccess.ReadWriteExecute:
254                                 return MmapProts.PROT_WRITE | MmapProts.PROT_READ | MmapProts.PROT_EXEC;
255                                 
256                         case MemoryMappedFileAccess.Read:
257                         default:
258                                 return MmapProts.PROT_READ;
259                         }
260                         
261                 }
262
263                 internal static unsafe void MapPosix (FileStream file, long offset, long size, MemoryMappedFileAccess access, out IntPtr map_addr, out int offset_diff)
264                 {
265                         if (pagesize == 0)
266                                 pagesize = Syscall.getpagesize ();
267
268                         long fsize = file.Length;
269
270                         if (size == 0 || size > fsize)
271                                 size = fsize;
272                         
273                         // Align offset
274                         long real_offset = offset & ~(pagesize - 1);
275
276                         offset_diff = (int)(offset - real_offset);
277
278                         // FIXME: Need to determine the unix fd for the file, Handle is only
279                         // equal to it by accident
280                         //
281                         // The new API no longer uses FileStream everywhere, but exposes instead
282                         // the filename (with one exception), we could move this API to use
283                         // file descriptors instead of the FileStream plus its Handle.
284                         //
285                         map_addr = Syscall.mmap (IntPtr.Zero, (ulong) size,
286                                                  ToUnixProts (access),
287                                                  access == MemoryMappedFileAccess.CopyOnWrite ? MmapFlags.MAP_PRIVATE : MmapFlags.MAP_SHARED,
288                                                  (int)file.Handle, real_offset);
289                         
290                         if (map_addr == (IntPtr)(-1))
291                                 throw new IOException ("mmap failed for " + file + "(" + offset + ", " + size + ")");
292                 }
293
294                 internal static void FlushPosix (FileStream file)
295                 {
296                         Syscall.fsync ((int) file.Handle);
297                 }
298                 
299                 internal static bool UnmapPosix (IntPtr map_addr, ulong map_size)
300                 {
301                         return Syscall.munmap (map_addr, map_size) == 0;
302                 }
303
304         }
305 }
306
307 #endif