2010-05-25 Jb Evain <jbevain@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 Mono.Unix;
37 using System.Runtime.InteropServices;
38
39 namespace System.IO.MemoryMappedFiles
40 {
41         public class MemoryMappedFile : IDisposable {
42                 MemoryMappedFileAccess fileAccess;
43                 string name;
44                 long fileCapacity;
45
46                 //
47                 // We allow the use of either the FileStream/keepOpen combo
48                 // or a Unix file descriptor.  This way we avoid the dependency on
49                 // Mono's io-layer having the Unix file descriptors mapped to
50                 // the same io-layer handle
51                 //
52                 FileStream stream;
53                 bool keepOpen;
54                 int unix_fd;
55                 
56                 public static MemoryMappedFile CreateFromFile (FileStream fileStream)
57                 {
58                         if (fileStream == null)
59                                 throw new ArgumentNullException ("fileStream");
60
61                         return new MemoryMappedFile () {
62                                 stream = fileStream,
63                                 fileAccess = MemoryMappedFileAccess.ReadWrite
64                         };
65                 }
66
67                 public static MemoryMappedFile CreateFromFile (string path)
68                 {
69                         return CreateFromFile (path, FileMode.Open, null, 0, MemoryMappedFileAccess.ReadWrite);
70                 }
71
72                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode)
73                 {
74                         return CreateFromFile (path, mode, null, 0, MemoryMappedFileAccess.ReadWrite);
75                 }
76
77                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName)
78                 {
79                         return CreateFromFile (path, mode, mapName, 0, MemoryMappedFileAccess.ReadWrite);
80                 }
81
82                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity)
83                 {
84                         return CreateFromFile (path, mode, mapName, capacity, MemoryMappedFileAccess.ReadWrite);
85                 }
86
87                 //
88                 // Turns the FileMode into the first half of open(2) flags
89                 //
90                 static OpenFlags ToUnixMode (FileMode mode)
91                 {
92                         switch (mode){
93                         case FileMode.CreateNew:
94                                 return OpenFlags.O_CREAT | OpenFlags.O_EXCL;
95                                 
96                         case FileMode.Create:
97                                 return OpenFlags.O_CREAT | OpenFlags.O_TRUNC;
98                                 
99                         case FileMode.OpenOrCreate:
100                                 return OpenFlags.O_CREAT;
101                                 
102                         case FileMode.Truncate:
103                                 return OpenFlags.O_TRUNC;
104                                 
105                         case FileMode.Append:
106                                 return OpenFlags.O_APPEND;
107                         default:
108                         case FileMode.Open:
109                                 return 0;
110                         }
111                 }
112
113                 //
114                 // Turns the MemoryMappedFileAccess into the second half of open(2) flags
115                 //
116                 static OpenFlags ToUnixMode (MemoryMappedFileAccess access)
117                 {
118                         switch (access){
119                         case MemoryMappedFileAccess.CopyOnWrite:
120                         case MemoryMappedFileAccess.ReadWriteExecute:
121                         case MemoryMappedFileAccess.ReadWrite:
122                                 return OpenFlags.O_RDWR;
123                                 
124                         case MemoryMappedFileAccess.Write:
125                                 return OpenFlags.O_WRONLY;
126
127                         case MemoryMappedFileAccess.ReadExecute:
128                         case MemoryMappedFileAccess.Read:
129                         default:
130                                 return OpenFlags.O_RDONLY;
131                         }
132                 }
133
134                 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity, MemoryMappedFileAccess access)
135                 {
136                         if (path == null)
137                                 throw new ArgumentNullException ("path");
138                         if (path.Length == 0)
139                                 throw new ArgumentException ("path");
140                         if (mapName != null && mapName.Length == 0)
141                                 throw new ArgumentException ("mapName");
142
143                         int fd;
144                         if (MonoUtil.IsUnix){
145                                 Stat buf;
146                                 if (Syscall.stat (path, out buf) == -1)
147                                         UnixMarshal.ThrowExceptionForLastError ();
148
149                                 if ((capacity == 0 && buf.st_size == 0) || (capacity > buf.st_size))
150                                         throw new ArgumentException ("capacity");
151                                 
152                                 fd = Syscall.open (path, ToUnixMode (mode) | ToUnixMode (access), FilePermissions.DEFFILEMODE);
153
154                                 if (fd == -1)
155                                         UnixMarshal.ThrowExceptionForLastError ();
156                         } else
157                                 throw new NotImplementedException ();
158                         
159                         return new MemoryMappedFile () {
160                                 unix_fd = fd,
161                                 fileAccess = access,
162                                 name = mapName,
163                                 fileCapacity = capacity
164                         };
165                 }
166
167                 static void ConfigureUnixFD (IntPtr handle, HandleInheritability h)
168                 {
169                         // TODO: Mono.Posix is lacking O_CLOEXEC definitions for fcntl.
170                 }
171
172
173                 [DllImport("kernel32.dll", SetLastError = true)]
174                 static extern bool SetHandleInformation (IntPtr hObject, int dwMask, int dwFlags);
175                 static void ConfigureWindowsFD (IntPtr handle, HandleInheritability h)
176                 {
177                         SetHandleInformation (handle, 1 /* FLAG_INHERIT */, h == HandleInheritability.None ? 0 : 1);
178                 }
179
180                 [MonoLimitation ("memoryMappedFileSecurity is currently ignored")]
181                 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
182                                                                MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability,
183                                                                bool leaveOpen)
184                 {
185                         if (fileStream == null)
186                                 throw new ArgumentNullException ("fileStream");
187                         if (mapName != null && mapName.Length == 0)
188                                 throw new ArgumentException ("mapName");
189                         if ((capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
190                                 throw new ArgumentException ("capacity");
191
192                         if (MonoUtil.IsUnix)
193                                 ConfigureUnixFD (fileStream.Handle, inheritability);
194                         else
195                                 ConfigureWindowsFD (fileStream.Handle, inheritability);
196                                 
197                         return new MemoryMappedFile () {
198                                 stream = fileStream,
199                                 fileAccess = access,
200                                 name = mapName,
201                                 fileCapacity = capacity,
202                                 keepOpen = leaveOpen
203                         };
204                 }
205
206                 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
207                 public static MemoryMappedFile CreateNew (string mapName, long capacity)
208                 {
209                         return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
210                 }
211
212                 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
213                 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access) 
214                 {
215                         return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
216                 }
217
218                 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix; options and memoryMappedFileSecurity are ignored")]
219                 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access,
220                                                           MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity,
221                                                           HandleInheritability handleInheritability)
222                 {
223                         return CreateFromFile (mapName, FileMode.CreateNew, mapName, capacity, access);
224                 }
225
226                 [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
227                 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity)
228                 {
229                         return CreateOrOpen (mapName, capacity, MemoryMappedFileAccess.ReadWrite);
230                 }
231
232                 [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
233                 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access)
234                 {
235                         return CreateFromFile (mapName, FileMode.OpenOrCreate, mapName, capacity, access); 
236                 }
237
238                 /*
239                 [MonoTODO]
240                         public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability handleInheritability) {
241                         throw new NotImplementedException ();
242                 }
243                 */
244
245                 public MemoryMappedViewStream CreateViewStream ()
246                 {
247                         return CreateViewStream (0, 0);
248                 }
249
250                 public MemoryMappedViewStream CreateViewStream (long offset, long size)
251                 {
252                         return CreateViewStream (offset, size, MemoryMappedFileAccess.ReadWrite);
253                 }
254
255                 public MemoryMappedViewStream CreateViewStream (long offset, long size, MemoryMappedFileAccess access)
256                 {
257                         return new MemoryMappedViewStream (stream != null ? (int)stream.Handle : unix_fd, offset, size, access);
258                 }
259
260                 public MemoryMappedViewAccessor CreateViewAccessor ()
261                 {
262                         return CreateViewAccessor (0, 0);
263                 }
264
265                 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size)
266                 {
267                         return CreateViewAccessor (offset, size, MemoryMappedFileAccess.ReadWrite);
268                 }
269
270                 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size, MemoryMappedFileAccess access)
271                 {
272                         int file_handle = stream != null ? (int) stream.Handle : unix_fd;
273                         
274                         return new MemoryMappedViewAccessor (file_handle, offset, size, access);
275                 }
276
277                 MemoryMappedFile ()
278                 {
279                 }
280
281                 public void Dispose ()
282                 {
283                         Dispose (true);
284                 }
285
286                 protected virtual void Dispose (bool disposing)
287                 {
288                         if (disposing){
289                                 if (stream != null){
290                                         if (keepOpen == false)
291                                                 stream.Close ();
292                                         unix_fd = -1;
293                                 }
294                                 if (unix_fd != -1)
295                                         Syscall.close (unix_fd);
296                                 unix_fd = -1;
297                                 stream = null;
298                         }
299                 }
300                 
301                 [MonoTODO]
302                 public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle {
303                         get {
304                                 throw new NotImplementedException ();
305                         }
306                 }
307
308                 static int pagesize;
309
310                 static MmapProts ToUnixProts (MemoryMappedFileAccess access)
311                 {
312                         MmapProts prots;
313                         
314                         switch (access){
315                         case MemoryMappedFileAccess.ReadWrite:
316                                 return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
317                                 break;
318                                 
319                         case MemoryMappedFileAccess.Write:
320                                 return MmapProts.PROT_WRITE;
321                                 
322                         case MemoryMappedFileAccess.CopyOnWrite:
323                                 return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
324                                 
325                         case MemoryMappedFileAccess.ReadExecute:
326                                 return MmapProts.PROT_EXEC;
327                                 
328                         case MemoryMappedFileAccess.ReadWriteExecute:
329                                 return MmapProts.PROT_WRITE | MmapProts.PROT_READ | MmapProts.PROT_EXEC;
330                                 
331                         case MemoryMappedFileAccess.Read:
332                         default:
333                                 return MmapProts.PROT_READ;
334                         }
335                 }
336
337                 internal static unsafe void MapPosix (int file_handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr map_addr, out int offset_diff)
338                 {
339                         if (pagesize == 0)
340                                 pagesize = Syscall.getpagesize ();
341
342                         Stat buf;
343                         Syscall.fstat (file_handle, out buf);
344                         long fsize = buf.st_size;
345
346                         if (size == 0 || size > fsize)
347                                 size = fsize;
348                         
349                         // Align offset
350                         long real_offset = offset & ~(pagesize - 1);
351
352                         offset_diff = (int)(offset - real_offset);
353
354                         // FIXME: Need to determine the unix fd for the file, Handle is only
355                         // equal to it by accident
356                         //
357                         // The new API no longer uses FileStream everywhere, but exposes instead
358                         // the filename (with one exception), we could move this API to use
359                         // file descriptors instead of the FileStream plus its Handle.
360                         //
361                         map_addr = Syscall.mmap (IntPtr.Zero, (ulong) size,
362                                                  ToUnixProts (access),
363                                                  access == MemoryMappedFileAccess.CopyOnWrite ? MmapFlags.MAP_PRIVATE : MmapFlags.MAP_SHARED,
364                                                  file_handle, real_offset);
365
366                         if (map_addr == (IntPtr)(-1))
367                                 throw new IOException ("mmap failed for fd#" + file_handle + "(" + offset + ", " + size + ")");
368                 }
369
370                 internal static bool UnmapPosix (IntPtr map_addr, ulong map_size)
371                 {
372                         return Syscall.munmap (map_addr, map_size) == 0;
373                 }
374
375         }
376 }
377
378 #endif