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 static extern int getpagesize ();
242 [MethodImplAttribute (MethodImplOptions.InternalCall)]
243 static extern long mono_filesize_from_path (string str);
245 [MethodImplAttribute (MethodImplOptions.InternalCall)]
246 static extern long mono_filesize_from_fd (int fd);
248 //Values valid on iOS/OSX and android ndk r6
249 const int F_GETFD = 1;
250 const int F_SETFD = 2;
251 const int FD_CLOEXEC = 1;
252 const int DEFFILEMODE = 0x666;
254 const int O_RDONLY = 0x0;
255 const int O_WRONLY = 0x1;
256 const int O_RDWR = 0x2;
258 const int PROT_READ = 0x1;
259 const int PROT_WRITE = 0x2;
260 const int PROT_EXEC = 0x4;
262 const int MAP_PRIVATE = 0x2;
263 const int MAP_SHARED = 0x1;
265 const int EINVAL = 22;
268 const int O_CREAT = 0x040;
269 const int O_TRUNC = 0x080;
270 const int O_EXCL = 0x200;
272 const int ENAMETOOLONG = 63;
275 const int O_CREAT = 0x0200;
276 const int O_TRUNC = 0x0400;
277 const int O_EXCL = 0x0800;
279 const int ENAMETOOLONG = 36;
282 static int ToUnixMode (FileMode mode)
285 case FileMode.CreateNew:
286 return O_CREAT | O_EXCL;
288 case FileMode.Create:
289 return O_CREAT | O_TRUNC;
291 case FileMode.OpenOrCreate:
294 case FileMode.Truncate:
303 // Turns the MemoryMappedFileAccess into the second half of open(2) flags
305 static int ToUnixMode (MemoryMappedFileAccess access)
308 case MemoryMappedFileAccess.CopyOnWrite:
309 case MemoryMappedFileAccess.ReadWriteExecute:
310 case MemoryMappedFileAccess.ReadWrite:
313 case MemoryMappedFileAccess.Write:
316 case MemoryMappedFileAccess.ReadExecute:
317 case MemoryMappedFileAccess.Read:
323 static int ToUnixProts (MemoryMappedFileAccess access)
326 case MemoryMappedFileAccess.ReadWrite:
327 return PROT_WRITE | PROT_READ;
329 case MemoryMappedFileAccess.Write:
332 case MemoryMappedFileAccess.CopyOnWrite:
333 return PROT_WRITE | PROT_READ;
335 case MemoryMappedFileAccess.ReadExecute:
338 case MemoryMappedFileAccess.ReadWriteExecute:
339 return PROT_WRITE | PROT_READ | PROT_EXEC;
341 case MemoryMappedFileAccess.Read:
347 static void ThrowErrorFromErrno (int errno)
350 case EINVAL: throw new ArgumentException ();
351 case ENAMETOOLONG: throw new PathTooLongException ();
352 default: throw new IOException ("Failed with errno " + errno);
356 internal static int Open (string path, FileMode mode, long capacity, MemoryMappedFileAccess access)
358 long file_size = mono_filesize_from_path (path);
360 throw new FileNotFoundException (path);
362 if ((capacity == 0 && file_size == 0) || (capacity > file_size))
363 throw new ArgumentException ("capacity");
365 int fd = open (path, ToUnixMode (mode) | ToUnixMode (access), DEFFILEMODE);
368 ThrowErrorFromErrno (Marshal.GetLastWin32Error ());
372 internal static void CloseFD (int fd)
377 internal static void Flush (int fd)
382 internal static bool Unmap (IntPtr map_addr, ulong map_size)
384 return munmap (map_addr, (IntPtr)map_size) == 0;
389 internal static unsafe void Map (int file_handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr map_addr, out int offset_diff)
392 pagesize = getpagesize ();
394 long fsize = mono_filesize_from_fd (file_handle);
396 throw new FileNotFoundException ();
398 if (size == 0 || size > fsize)
402 long real_offset = offset & ~(pagesize - 1);
404 offset_diff = (int)(offset - real_offset);
406 map_addr = mmap (IntPtr.Zero, (IntPtr) size,
407 ToUnixProts (access),
408 access == MemoryMappedFileAccess.CopyOnWrite ? MAP_PRIVATE : MAP_SHARED,
409 file_handle, real_offset);
411 if (map_addr == (IntPtr)(-1))
412 throw new IOException ("mmap failed for fd#" + file_handle + "(" + offset + ", " + size + ")");
415 internal static void ConfigureFD (IntPtr handle, HandleInheritability inheritability)
417 int fd = (int)handle;
418 int flags = fcntl (fd, F_GETFD, 0);
419 if (inheritability == HandleInheritability.None)
420 flags &= ~FD_CLOEXEC;
423 fcntl (fd, F_SETFD, flags);
429 public class MemoryMappedFile : IDisposable {
430 MemoryMappedFileAccess fileAccess;
435 // We allow the use of either the FileStream/keepOpen combo
436 // or a Unix file descriptor. This way we avoid the dependency on
437 // Mono's io-layer having the Unix file descriptors mapped to
438 // the same io-layer handle
444 public static MemoryMappedFile CreateFromFile (string path)
446 return CreateFromFile (path, FileMode.Open, null, 0, MemoryMappedFileAccess.ReadWrite);
449 public static MemoryMappedFile CreateFromFile (string path, FileMode mode)
451 return CreateFromFile (path, mode, null, 0, MemoryMappedFileAccess.ReadWrite);
454 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName)
456 return CreateFromFile (path, mode, mapName, 0, MemoryMappedFileAccess.ReadWrite);
459 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity)
461 return CreateFromFile (path, mode, mapName, capacity, MemoryMappedFileAccess.ReadWrite);
464 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity, MemoryMappedFileAccess access)
467 throw new ArgumentNullException ("path");
468 if (path.Length == 0)
469 throw new ArgumentException ("path");
470 if (mapName != null && mapName.Length == 0)
471 throw new ArgumentException ("mapName");
472 if (mode == FileMode.Append)
473 throw new ArgumentException ("mode");
475 int fd = MemoryMapImpl.Open (path, mode, capacity, access);
477 return new MemoryMappedFile () {
481 fileCapacity = capacity
486 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
487 HandleInheritability inheritability,
490 [MonoLimitation ("memoryMappedFileSecurity is currently ignored")]
491 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
492 MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability,
496 if (fileStream == null)
497 throw new ArgumentNullException ("fileStream");
498 if (mapName != null && mapName.Length == 0)
499 throw new ArgumentException ("mapName");
500 if ((capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
501 throw new ArgumentException ("capacity");
503 MemoryMapImpl.ConfigureFD (fileStream.Handle, inheritability);
505 return new MemoryMappedFile () {
509 fileCapacity = capacity,
514 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
515 public static MemoryMappedFile CreateNew (string mapName, long capacity)
518 return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, 0);
520 return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
524 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
525 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access)
528 return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, 0);
530 return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
535 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access,
536 MemoryMappedFileOptions options,
537 HandleInheritability handleInheritability)
539 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix; options and memoryMappedFileSecurity are ignored")]
540 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access,
541 MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity,
542 HandleInheritability inheritability)
545 return CreateFromFile (mapName, FileMode.CreateNew, mapName, capacity, access);
548 [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
549 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity)
551 return CreateOrOpen (mapName, capacity, MemoryMappedFileAccess.ReadWrite);
554 [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
555 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access)
557 return CreateFromFile (mapName, FileMode.OpenOrCreate, mapName, capacity, access);
562 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, HandleInheritability inheritability)
564 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability)
567 throw new NotImplementedException ();
571 public static MemoryMappedFile OpenExisting (string mapName)
573 throw new NotImplementedException ();
577 public static MemoryMappedFile OpenExisting (string mapName, MemoryMappedFileRights desiredAccessRights)
579 throw new NotImplementedException ();
583 public static MemoryMappedFile OpenExisting (string mapName, MemoryMappedFileRights desiredAccessRights, HandleInheritability inheritability)
585 throw new NotImplementedException ();
588 public MemoryMappedViewStream CreateViewStream ()
590 return CreateViewStream (0, 0);
593 public MemoryMappedViewStream CreateViewStream (long offset, long size)
595 return CreateViewStream (offset, size, MemoryMappedFileAccess.ReadWrite);
598 public MemoryMappedViewStream CreateViewStream (long offset, long size, MemoryMappedFileAccess access)
600 return new MemoryMappedViewStream (stream != null ? (int)stream.Handle : unix_fd, offset, size, access);
603 public MemoryMappedViewAccessor CreateViewAccessor ()
605 return CreateViewAccessor (0, 0);
608 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size)
610 return CreateViewAccessor (offset, size, MemoryMappedFileAccess.ReadWrite);
613 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size, MemoryMappedFileAccess access)
615 int file_handle = stream != null ? (int) stream.Handle : unix_fd;
617 return new MemoryMappedViewAccessor (file_handle, offset, size, access);
624 public void Dispose ()
629 protected virtual void Dispose (bool disposing)
633 if (keepOpen == false)
639 MemoryMapImpl.CloseFD (unix_fd);
647 public MemoryMappedFileSecurity GetAccessControl ()
649 throw new NotImplementedException ();
653 public void SetAccessControl (MemoryMappedFileSecurity memoryMappedFileSecurity)
655 throw new NotImplementedException ();
660 public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle {
662 throw new NotImplementedException ();