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