5 // Zoltan Varga (vargaz@gmail.com)
7 // Copyright (C) 2009, Novell, Inc (http://www.novell.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
33 using System.Collections.Generic;
34 using Microsoft.Win32.SafeHandles;
35 using System.Runtime.InteropServices;
39 using Mono.Unix.Native;
42 using System.Runtime.CompilerServices;
45 namespace System.IO.MemoryMappedFiles
48 internal static class MemoryMapImpl {
50 // Turns the FileMode into the first half of open(2) flags
52 static OpenFlags ToUnixMode (FileMode mode)
55 case FileMode.CreateNew:
56 return OpenFlags.O_CREAT | OpenFlags.O_EXCL;
59 return OpenFlags.O_CREAT | OpenFlags.O_TRUNC;
61 case FileMode.OpenOrCreate:
62 return OpenFlags.O_CREAT;
64 case FileMode.Truncate:
65 return OpenFlags.O_TRUNC;
68 return OpenFlags.O_APPEND;
76 // Turns the MemoryMappedFileAccess into the second half of open(2) flags
78 static OpenFlags ToUnixMode (MemoryMappedFileAccess access)
81 case MemoryMappedFileAccess.CopyOnWrite:
82 case MemoryMappedFileAccess.ReadWriteExecute:
83 case MemoryMappedFileAccess.ReadWrite:
84 return OpenFlags.O_RDWR;
86 case MemoryMappedFileAccess.Write:
87 return OpenFlags.O_WRONLY;
89 case MemoryMappedFileAccess.ReadExecute:
90 case MemoryMappedFileAccess.Read:
92 return OpenFlags.O_RDONLY;
96 static MmapProts ToUnixProts (MemoryMappedFileAccess access)
99 case MemoryMappedFileAccess.ReadWrite:
100 return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
102 case MemoryMappedFileAccess.Write:
103 return MmapProts.PROT_WRITE;
105 case MemoryMappedFileAccess.CopyOnWrite:
106 return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
108 case MemoryMappedFileAccess.ReadExecute:
109 return MmapProts.PROT_EXEC;
111 case MemoryMappedFileAccess.ReadWriteExecute:
112 return MmapProts.PROT_WRITE | MmapProts.PROT_READ | MmapProts.PROT_EXEC;
114 case MemoryMappedFileAccess.Read:
116 return MmapProts.PROT_READ;
120 internal static int Open (string path, FileMode mode, long capacity, MemoryMappedFileAccess access)
122 if (MonoUtil.IsUnix){
124 if (Syscall.stat (path, out buf) == -1)
125 UnixMarshal.ThrowExceptionForLastError ();
127 if ((capacity == 0 && buf.st_size == 0) || (capacity > buf.st_size))
128 throw new ArgumentException ("capacity");
130 int fd = Syscall.open (path, ToUnixMode (mode) | ToUnixMode (access), FilePermissions.DEFFILEMODE);
133 UnixMarshal.ThrowExceptionForLastError ();
136 throw new NotImplementedException ();
139 internal static void CloseFD (int fd) {
143 internal static void Flush (int fd) {
147 throw new NotImplementedException ("Not implemented on Windows");
153 internal static unsafe void Map (int file_handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr map_addr, out int offset_diff)
155 if (!MonoUtil.IsUnix)
156 throw new NotImplementedException ("Not implemented on windows.");
159 pagesize = Syscall.getpagesize ();
162 Syscall.fstat (file_handle, out buf);
163 long fsize = buf.st_size;
165 if (size == 0 || size > fsize)
169 long real_offset = offset & ~(pagesize - 1);
171 offset_diff = (int)(offset - real_offset);
173 // FIXME: Need to determine the unix fd for the file, Handle is only
174 // equal to it by accident
176 // The new API no longer uses FileStream everywhere, but exposes instead
177 // the filename (with one exception), we could move this API to use
178 // file descriptors instead of the FileStream plus its Handle.
180 map_addr = Syscall.mmap (IntPtr.Zero, (ulong) size,
181 ToUnixProts (access),
182 access == MemoryMappedFileAccess.CopyOnWrite ? MmapFlags.MAP_PRIVATE : MmapFlags.MAP_SHARED,
183 file_handle, real_offset);
185 if (map_addr == (IntPtr)(-1))
186 throw new IOException ("mmap failed for fd#" + file_handle + "(" + offset + ", " + size + ")");
189 internal static bool Unmap (IntPtr map_addr, ulong map_size)
191 if (!MonoUtil.IsUnix)
193 return Syscall.munmap (map_addr, map_size) == 0;
196 static void ConfigureUnixFD (IntPtr handle, HandleInheritability h)
198 // TODO: Mono.Posix is lacking O_CLOEXEC definitions for fcntl.
202 [DllImport("kernel32", SetLastError = true)]
203 static extern bool SetHandleInformation (IntPtr hObject, int dwMask, int dwFlags);
204 static void ConfigureWindowsFD (IntPtr handle, HandleInheritability h)
206 SetHandleInformation (handle, 1 /* FLAG_INHERIT */, h == HandleInheritability.None ? 0 : 1);
209 internal static void ConfigureFD (IntPtr handle, HandleInheritability inheritability)
212 ConfigureUnixFD (handle, inheritability);
214 ConfigureWindowsFD (handle, inheritability);
219 internal static class MemoryMapImpl {
221 static extern int fsync (int fd);
224 static extern int close (int fd);
227 static extern int fcntl (int fd, int cmd, int arg0);
229 //XXX check if android off_t is 64bits or not. on iOS / darwin it is.
231 static extern IntPtr mmap (IntPtr addr, IntPtr len, int prot, int flags, int fd, long offset);
234 static extern int munmap (IntPtr addr, IntPtr size);
236 [DllImport ("libc", SetLastError=true)]
237 static extern int open (string path, int flags, int access);
240 [DllImport ("__Internal")]
241 static extern int monodroid_getpagesize ();
243 static int getpagesize ()
245 return monodroid_getpagesize ();
249 static extern int getpagesize ();
252 [MethodImplAttribute (MethodImplOptions.InternalCall)]
253 static extern long mono_filesize_from_path (string str);
255 [MethodImplAttribute (MethodImplOptions.InternalCall)]
256 static extern long mono_filesize_from_fd (int fd);
258 //Values valid on iOS/OSX and android ndk r6
259 const int F_GETFD = 1;
260 const int F_SETFD = 2;
261 const int FD_CLOEXEC = 1;
262 const int DEFFILEMODE = 0x666;
264 const int O_RDONLY = 0x0;
265 const int O_WRONLY = 0x1;
266 const int O_RDWR = 0x2;
268 const int PROT_READ = 0x1;
269 const int PROT_WRITE = 0x2;
270 const int PROT_EXEC = 0x4;
272 const int MAP_PRIVATE = 0x2;
273 const int MAP_SHARED = 0x1;
275 const int EINVAL = 22;
278 const int O_CREAT = 0x040;
279 const int O_TRUNC = 0x080;
280 const int O_EXCL = 0x200;
282 const int ENAMETOOLONG = 63;
285 const int O_CREAT = 0x0200;
286 const int O_TRUNC = 0x0400;
287 const int O_EXCL = 0x0800;
289 const int ENAMETOOLONG = 36;
292 static int ToUnixMode (FileMode mode)
295 case FileMode.CreateNew:
296 return O_CREAT | O_EXCL;
298 case FileMode.Create:
299 return O_CREAT | O_TRUNC;
301 case FileMode.OpenOrCreate:
304 case FileMode.Truncate:
313 // Turns the MemoryMappedFileAccess into the second half of open(2) flags
315 static int ToUnixMode (MemoryMappedFileAccess access)
318 case MemoryMappedFileAccess.CopyOnWrite:
319 case MemoryMappedFileAccess.ReadWriteExecute:
320 case MemoryMappedFileAccess.ReadWrite:
323 case MemoryMappedFileAccess.Write:
326 case MemoryMappedFileAccess.ReadExecute:
327 case MemoryMappedFileAccess.Read:
333 static int ToUnixProts (MemoryMappedFileAccess access)
336 case MemoryMappedFileAccess.ReadWrite:
337 return PROT_WRITE | PROT_READ;
339 case MemoryMappedFileAccess.Write:
342 case MemoryMappedFileAccess.CopyOnWrite:
343 return PROT_WRITE | PROT_READ;
345 case MemoryMappedFileAccess.ReadExecute:
348 case MemoryMappedFileAccess.ReadWriteExecute:
349 return PROT_WRITE | PROT_READ | PROT_EXEC;
351 case MemoryMappedFileAccess.Read:
357 static void ThrowErrorFromErrno (int errno)
360 case EINVAL: throw new ArgumentException ();
361 case ENAMETOOLONG: throw new PathTooLongException ();
362 default: throw new IOException ("Failed with errno " + errno);
366 internal static int Open (string path, FileMode mode, long capacity, MemoryMappedFileAccess access)
368 long file_size = mono_filesize_from_path (path);
370 throw new FileNotFoundException (path);
372 if ((capacity == 0 && file_size == 0) || (capacity > file_size))
373 throw new ArgumentException ("capacity");
375 int fd = open (path, ToUnixMode (mode) | ToUnixMode (access), DEFFILEMODE);
378 ThrowErrorFromErrno (Marshal.GetLastWin32Error ());
382 internal static void CloseFD (int fd)
387 internal static void Flush (int fd)
392 internal static bool Unmap (IntPtr map_addr, ulong map_size)
394 return munmap (map_addr, (IntPtr)map_size) == 0;
399 internal static unsafe void Map (int file_handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr map_addr, out int offset_diff)
402 pagesize = getpagesize ();
404 long fsize = mono_filesize_from_fd (file_handle);
406 throw new FileNotFoundException ();
408 if (size == 0 || size > fsize)
412 long real_offset = offset & ~(pagesize - 1);
414 offset_diff = (int)(offset - real_offset);
416 map_addr = mmap (IntPtr.Zero, (IntPtr) size,
417 ToUnixProts (access),
418 access == MemoryMappedFileAccess.CopyOnWrite ? MAP_PRIVATE : MAP_SHARED,
419 file_handle, real_offset);
421 if (map_addr == (IntPtr)(-1))
422 throw new IOException ("mmap failed for fd#" + file_handle + "(" + offset + ", " + size + ")");
425 internal static void ConfigureFD (IntPtr handle, HandleInheritability inheritability)
427 int fd = (int)handle;
428 int flags = fcntl (fd, F_GETFD, 0);
429 if (inheritability == HandleInheritability.None)
430 flags &= ~FD_CLOEXEC;
433 fcntl (fd, F_SETFD, flags);
439 public class MemoryMappedFile : IDisposable {
440 MemoryMappedFileAccess fileAccess;
445 // We allow the use of either the FileStream/keepOpen combo
446 // or a Unix file descriptor. This way we avoid the dependency on
447 // Mono's io-layer having the Unix file descriptors mapped to
448 // the same io-layer handle
454 public static MemoryMappedFile CreateFromFile (string path)
456 return CreateFromFile (path, FileMode.Open, null, 0, MemoryMappedFileAccess.ReadWrite);
459 public static MemoryMappedFile CreateFromFile (string path, FileMode mode)
461 return CreateFromFile (path, mode, null, 0, MemoryMappedFileAccess.ReadWrite);
464 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName)
466 return CreateFromFile (path, mode, mapName, 0, MemoryMappedFileAccess.ReadWrite);
469 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity)
471 return CreateFromFile (path, mode, mapName, capacity, MemoryMappedFileAccess.ReadWrite);
474 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity, MemoryMappedFileAccess access)
477 throw new ArgumentNullException ("path");
478 if (path.Length == 0)
479 throw new ArgumentException ("path");
480 if (mapName != null && mapName.Length == 0)
481 throw new ArgumentException ("mapName");
482 if (mode == FileMode.Append)
483 throw new ArgumentException ("mode");
485 int fd = MemoryMapImpl.Open (path, mode, capacity, access);
487 return new MemoryMappedFile () {
491 fileCapacity = capacity
496 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
497 HandleInheritability inheritability,
500 [MonoLimitation ("memoryMappedFileSecurity is currently ignored")]
501 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
502 MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability,
506 if (fileStream == null)
507 throw new ArgumentNullException ("fileStream");
508 if (mapName != null && mapName.Length == 0)
509 throw new ArgumentException ("mapName");
510 if ((capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
511 throw new ArgumentException ("capacity");
513 MemoryMapImpl.ConfigureFD (fileStream.Handle, inheritability);
515 return new MemoryMappedFile () {
519 fileCapacity = capacity,
524 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
525 public static MemoryMappedFile CreateNew (string mapName, long capacity)
528 return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, 0);
530 return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
534 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
535 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access)
538 return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, 0);
540 return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
545 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access,
546 MemoryMappedFileOptions options,
547 HandleInheritability handleInheritability)
549 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix; options and memoryMappedFileSecurity are ignored")]
550 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access,
551 MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity,
552 HandleInheritability inheritability)
555 return CreateFromFile (mapName, FileMode.CreateNew, mapName, capacity, access);
558 [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
559 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity)
561 return CreateOrOpen (mapName, capacity, MemoryMappedFileAccess.ReadWrite);
564 [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
565 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access)
567 return CreateFromFile (mapName, FileMode.OpenOrCreate, mapName, capacity, access);
572 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, HandleInheritability inheritability)
574 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability)
577 throw new NotImplementedException ();
581 public static MemoryMappedFile OpenExisting (string mapName)
583 throw new NotImplementedException ();
587 public static MemoryMappedFile OpenExisting (string mapName, MemoryMappedFileRights desiredAccessRights)
589 throw new NotImplementedException ();
593 public static MemoryMappedFile OpenExisting (string mapName, MemoryMappedFileRights desiredAccessRights, HandleInheritability inheritability)
595 throw new NotImplementedException ();
598 public MemoryMappedViewStream CreateViewStream ()
600 return CreateViewStream (0, 0);
603 public MemoryMappedViewStream CreateViewStream (long offset, long size)
605 return CreateViewStream (offset, size, MemoryMappedFileAccess.ReadWrite);
608 public MemoryMappedViewStream CreateViewStream (long offset, long size, MemoryMappedFileAccess access)
610 return new MemoryMappedViewStream (stream != null ? (int)stream.Handle : unix_fd, offset, size, access);
613 public MemoryMappedViewAccessor CreateViewAccessor ()
615 return CreateViewAccessor (0, 0);
618 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size)
620 return CreateViewAccessor (offset, size, MemoryMappedFileAccess.ReadWrite);
623 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size, MemoryMappedFileAccess access)
625 int file_handle = stream != null ? (int) stream.Handle : unix_fd;
627 return new MemoryMappedViewAccessor (file_handle, offset, size, access);
634 public void Dispose ()
639 protected virtual void Dispose (bool disposing)
643 if (keepOpen == false)
649 MemoryMapImpl.CloseFD (unix_fd);
657 public MemoryMappedFileSecurity GetAccessControl ()
659 throw new NotImplementedException ();
663 public void SetAccessControl (MemoryMappedFileSecurity memoryMappedFileSecurity)
665 throw new NotImplementedException ();
670 public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle {
672 throw new NotImplementedException ();