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.
32 using System.Collections.Generic;
33 using Microsoft.Win32.SafeHandles;
34 using System.Runtime.InteropServices;
38 using Mono.Unix.Native;
41 using System.Runtime.CompilerServices;
44 namespace System.IO.MemoryMappedFiles
46 internal static partial class MemoryMapImpl {
47 static Exception ArgumentCapacity ()
49 return new ArgumentException ("A positive capacity must be specified for a Memory Mapped File backed by an empty file.");
52 static Exception CapacitySmallerThanSize ()
54 return new ArgumentOutOfRangeException ("The capacity may not be smaller than the file size.");
60 partial class MemoryMapImpl {
62 // Turns the FileMode into the first half of open(2) flags
64 static OpenFlags ToUnixMode (FileMode mode)
67 case FileMode.CreateNew:
68 return OpenFlags.O_CREAT | OpenFlags.O_EXCL;
71 return OpenFlags.O_CREAT | OpenFlags.O_TRUNC;
73 case FileMode.OpenOrCreate:
74 return OpenFlags.O_CREAT;
76 case FileMode.Truncate:
77 return OpenFlags.O_TRUNC;
80 return OpenFlags.O_APPEND;
88 // Turns the MemoryMappedFileAccess into the second half of open(2) flags
90 static OpenFlags ToUnixMode (MemoryMappedFileAccess access)
93 case MemoryMappedFileAccess.CopyOnWrite:
94 case MemoryMappedFileAccess.ReadWriteExecute:
95 case MemoryMappedFileAccess.ReadWrite:
96 return OpenFlags.O_RDWR;
98 case MemoryMappedFileAccess.Write:
99 return OpenFlags.O_WRONLY;
101 case MemoryMappedFileAccess.ReadExecute:
102 case MemoryMappedFileAccess.Read:
104 return OpenFlags.O_RDONLY;
108 static MmapProts ToUnixProts (MemoryMappedFileAccess access)
111 case MemoryMappedFileAccess.ReadWrite:
112 return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
114 case MemoryMappedFileAccess.Write:
115 return MmapProts.PROT_WRITE;
117 case MemoryMappedFileAccess.CopyOnWrite:
118 return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
120 case MemoryMappedFileAccess.ReadExecute:
121 return MmapProts.PROT_EXEC;
123 case MemoryMappedFileAccess.ReadWriteExecute:
124 return MmapProts.PROT_WRITE | MmapProts.PROT_READ | MmapProts.PROT_EXEC;
126 case MemoryMappedFileAccess.Read:
128 return MmapProts.PROT_READ;
132 internal static int Open (string path, FileMode mode, ref long capacity, MemoryMappedFileAccess access)
134 if (MonoUtil.IsUnix){
137 int result = Syscall.stat (path, out buf);
139 if (mode == FileMode.Truncate || mode == FileMode.Append || mode == FileMode.Open){
141 UnixMarshal.ThrowExceptionForLastError ();
143 if (mode == FileMode.CreateNew && result == 0)
144 throw new IOException ("The file already exists");
148 // Special files such as FIFOs, sockets, and devices can
149 // have a size of 0. Specifying a capacity for these
150 // also makes little sense, so don't do the check if the
151 // file is one of these.
152 if (buf.st_size == 0 &&
153 (buf.st_mode & (FilePermissions.S_IFCHR |
154 FilePermissions.S_IFBLK |
155 FilePermissions.S_IFIFO |
156 FilePermissions.S_IFSOCK)) == 0) {
157 throw ArgumentCapacity ();
160 capacity = buf.st_size;
161 } else if (capacity < buf.st_size) {
162 throw CapacitySmallerThanSize ();
165 if (mode == FileMode.CreateNew){
167 throw ArgumentCapacity ();
171 int fd = Syscall.open (path, ToUnixMode (mode) | ToUnixMode (access), FilePermissions.DEFFILEMODE);
174 UnixMarshal.ThrowExceptionForLastError ();
178 throw new NotImplementedException ();
181 internal static void CloseFD (int fd) {
185 internal static void Flush (int fd) {
189 throw new NotImplementedException ("Not implemented on Windows");
195 internal static unsafe void Map (int file_handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr map_addr, out int offset_diff)
197 if (!MonoUtil.IsUnix)
198 throw new NotImplementedException ("Not implemented on windows.");
201 pagesize = Syscall.getpagesize ();
204 Syscall.fstat (file_handle, out buf);
205 long fsize = buf.st_size;
207 if (size == 0 || size > fsize)
211 long real_offset = offset & ~(pagesize - 1);
213 offset_diff = (int)(offset - real_offset);
215 // FIXME: Need to determine the unix fd for the file, Handle is only
216 // equal to it by accident
218 // The new API no longer uses FileStream everywhere, but exposes instead
219 // the filename (with one exception), we could move this API to use
220 // file descriptors instead of the FileStream plus its Handle.
222 map_addr = Syscall.mmap (IntPtr.Zero, (ulong) size,
223 ToUnixProts (access),
224 access == MemoryMappedFileAccess.CopyOnWrite ? MmapFlags.MAP_PRIVATE : MmapFlags.MAP_SHARED,
225 file_handle, real_offset);
227 if (map_addr == (IntPtr)(-1))
228 throw new IOException ("mmap failed for fd#" + file_handle + "(" + offset + ", " + size + ")");
231 internal static bool Unmap (IntPtr map_addr, ulong map_size)
233 if (!MonoUtil.IsUnix)
235 return Syscall.munmap (map_addr, map_size) == 0;
238 static void ConfigureUnixFD (IntPtr handle, HandleInheritability h)
240 // TODO: Mono.Posix is lacking O_CLOEXEC definitions for fcntl.
244 [DllImport("kernel32", SetLastError = true)]
245 static extern bool SetHandleInformation (IntPtr hObject, int dwMask, int dwFlags);
246 static void ConfigureWindowsFD (IntPtr handle, HandleInheritability h)
248 SetHandleInformation (handle, 1 /* FLAG_INHERIT */, h == HandleInheritability.None ? 0 : 1);
251 internal static void ConfigureFD (IntPtr handle, HandleInheritability inheritability)
254 ConfigureUnixFD (handle, inheritability);
256 ConfigureWindowsFD (handle, inheritability);
261 partial class MemoryMapImpl {
263 static extern int fsync (int fd);
266 static extern int close (int fd);
269 static extern int fcntl (int fd, int cmd, int arg0);
271 //XXX check if android off_t is 64bits or not. on iOS / darwin it is.
273 static extern IntPtr mmap (IntPtr addr, IntPtr len, int prot, int flags, int fd, long offset);
276 static extern int munmap (IntPtr addr, IntPtr size);
278 [DllImport ("libc", SetLastError=true)]
279 static extern int open (string path, int flags, int access);
282 [DllImport ("__Internal")]
283 static extern int monodroid_getpagesize ();
285 static int getpagesize ()
287 return monodroid_getpagesize ();
291 static extern int getpagesize ();
294 [MethodImplAttribute (MethodImplOptions.InternalCall)]
295 static extern long mono_filesize_from_path (string str);
297 [MethodImplAttribute (MethodImplOptions.InternalCall)]
298 static extern long mono_filesize_from_fd (int fd);
300 //Values valid on iOS/OSX and android ndk r6
301 const int F_GETFD = 1;
302 const int F_SETFD = 2;
303 const int FD_CLOEXEC = 1;
304 const int DEFFILEMODE = 0x666;
306 const int O_RDONLY = 0x0;
307 const int O_WRONLY = 0x1;
308 const int O_RDWR = 0x2;
310 const int PROT_READ = 0x1;
311 const int PROT_WRITE = 0x2;
312 const int PROT_EXEC = 0x4;
314 const int MAP_PRIVATE = 0x2;
315 const int MAP_SHARED = 0x1;
317 const int EINVAL = 22;
320 const int O_CREAT = 0x040;
321 const int O_TRUNC = 0x080;
322 const int O_EXCL = 0x200;
324 const int ENAMETOOLONG = 63;
326 /* MONOTOUCH - usr/include/sys/fcntl.h */
327 const int O_CREAT = 0x0200;
328 const int O_TRUNC = 0x0400;
329 const int O_EXCL = 0x0800;
331 // usr/include/sys/errno.h
332 const int ENAMETOOLONG = 63;
335 static int ToUnixMode (FileMode mode)
338 case FileMode.CreateNew:
339 return O_CREAT | O_EXCL;
341 case FileMode.Create:
342 return O_CREAT | O_TRUNC;
344 case FileMode.OpenOrCreate:
347 case FileMode.Truncate:
356 // Turns the MemoryMappedFileAccess into the second half of open(2) flags
358 static int ToUnixMode (MemoryMappedFileAccess access)
361 case MemoryMappedFileAccess.CopyOnWrite:
362 case MemoryMappedFileAccess.ReadWriteExecute:
363 case MemoryMappedFileAccess.ReadWrite:
366 case MemoryMappedFileAccess.Write:
369 case MemoryMappedFileAccess.ReadExecute:
370 case MemoryMappedFileAccess.Read:
376 static int ToUnixProts (MemoryMappedFileAccess access)
379 case MemoryMappedFileAccess.ReadWrite:
380 return PROT_WRITE | PROT_READ;
382 case MemoryMappedFileAccess.Write:
385 case MemoryMappedFileAccess.CopyOnWrite:
386 return PROT_WRITE | PROT_READ;
388 case MemoryMappedFileAccess.ReadExecute:
391 case MemoryMappedFileAccess.ReadWriteExecute:
392 return PROT_WRITE | PROT_READ | PROT_EXEC;
394 case MemoryMappedFileAccess.Read:
400 static void ThrowErrorFromErrno (int errno)
403 case EINVAL: throw new ArgumentException ();
404 case ENAMETOOLONG: throw new PathTooLongException ();
405 default: throw new IOException ("Failed with errno " + errno);
409 internal static int Open (string path, FileMode mode, ref long capacity, MemoryMappedFileAccess access)
411 long file_size = mono_filesize_from_path (path);
413 Console.WriteLine ("{0} is {1} big", path, file_size);
414 if (mode == FileMode.Truncate || mode == FileMode.Append || mode == FileMode.Open){
416 throw new FileNotFoundException (path);
418 capacity = file_size;
419 if (capacity < file_size)
420 throw CapacitySmallerThanSize ();
422 if (mode == FileMode.CreateNew){
424 throw new IOException ("The file already exists");
426 throw ArgumentCapacity ();
430 int fd = open (path, ToUnixMode (mode) | ToUnixMode (access), DEFFILEMODE);
433 ThrowErrorFromErrno (Marshal.GetLastWin32Error ());
437 internal static void CloseFD (int fd)
442 internal static void Flush (int fd)
447 internal static bool Unmap (IntPtr map_addr, ulong map_size)
449 return munmap (map_addr, (IntPtr)map_size) == 0;
454 internal static unsafe void Map (int file_handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr map_addr, out int offset_diff)
457 pagesize = getpagesize ();
459 long fsize = mono_filesize_from_fd (file_handle);
461 throw new FileNotFoundException ();
463 if (size == 0 || size > fsize)
467 long real_offset = offset & ~(pagesize - 1);
469 offset_diff = (int)(offset - real_offset);
471 map_addr = mmap (IntPtr.Zero, (IntPtr) size,
472 ToUnixProts (access),
473 access == MemoryMappedFileAccess.CopyOnWrite ? MAP_PRIVATE : MAP_SHARED,
474 file_handle, real_offset);
476 if (map_addr == (IntPtr)(-1))
477 throw new IOException ("mmap failed for fd#" + file_handle + "(" + offset + ", " + size + ")");
480 internal static void ConfigureFD (IntPtr handle, HandleInheritability inheritability)
482 int fd = (int)handle;
483 int flags = fcntl (fd, F_GETFD, 0);
484 if (inheritability == HandleInheritability.None)
485 flags &= ~FD_CLOEXEC;
488 fcntl (fd, F_SETFD, flags);
494 public class MemoryMappedFile : IDisposable {
495 MemoryMappedFileAccess fileAccess;
500 // We allow the use of either the FileStream/keepOpen combo
501 // or a Unix file descriptor. This way we avoid the dependency on
502 // Mono's io-layer having the Unix file descriptors mapped to
503 // the same io-layer handle
509 public static MemoryMappedFile CreateFromFile (string path)
511 return CreateFromFile (path, FileMode.Open, null, 0, MemoryMappedFileAccess.ReadWrite);
514 public static MemoryMappedFile CreateFromFile (string path, FileMode mode)
516 return CreateFromFile (path, mode, null, 0, MemoryMappedFileAccess.ReadWrite);
519 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName)
521 return CreateFromFile (path, mode, mapName, 0, MemoryMappedFileAccess.ReadWrite);
524 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity)
526 return CreateFromFile (path, mode, mapName, capacity, MemoryMappedFileAccess.ReadWrite);
529 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity, MemoryMappedFileAccess access)
532 throw new ArgumentNullException ("path");
533 if (path.Length == 0)
534 throw new ArgumentException ("path");
535 if (mapName != null && mapName.Length == 0)
536 throw new ArgumentException ("mapName");
537 if (mode == FileMode.Append)
538 throw new ArgumentException ("mode");
540 throw new ArgumentOutOfRangeException ("capacity");
542 int fd = MemoryMapImpl.Open (path, mode, ref capacity, access);
544 return new MemoryMappedFile () {
548 fileCapacity = capacity
553 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
554 HandleInheritability inheritability,
557 [MonoLimitation ("memoryMappedFileSecurity is currently ignored")]
558 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
559 MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability,
563 if (fileStream == null)
564 throw new ArgumentNullException ("fileStream");
565 if (mapName != null && mapName.Length == 0)
566 throw new ArgumentException ("mapName");
567 if ((!MonoUtil.IsUnix && capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
568 throw new ArgumentException ("capacity");
570 MemoryMapImpl.ConfigureFD (fileStream.Handle, inheritability);
572 return new MemoryMappedFile () {
576 fileCapacity = capacity,
581 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
582 public static MemoryMappedFile CreateNew (string mapName, long capacity)
585 return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, 0);
587 return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
591 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
592 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access)
595 return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, 0);
597 return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
602 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access,
603 MemoryMappedFileOptions options,
604 HandleInheritability handleInheritability)
606 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix; options and memoryMappedFileSecurity are ignored")]
607 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access,
608 MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity,
609 HandleInheritability inheritability)
612 return CreateFromFile (mapName, FileMode.CreateNew, mapName, capacity, access);
615 [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
616 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity)
618 return CreateOrOpen (mapName, capacity, MemoryMappedFileAccess.ReadWrite);
621 [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
622 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access)
624 return CreateFromFile (mapName, FileMode.OpenOrCreate, mapName, capacity, access);
629 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, HandleInheritability inheritability)
631 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability)
634 throw new NotImplementedException ();
638 public static MemoryMappedFile OpenExisting (string mapName)
640 throw new NotImplementedException ();
644 public static MemoryMappedFile OpenExisting (string mapName, MemoryMappedFileRights desiredAccessRights)
646 throw new NotImplementedException ();
650 public static MemoryMappedFile OpenExisting (string mapName, MemoryMappedFileRights desiredAccessRights, HandleInheritability inheritability)
652 throw new NotImplementedException ();
655 public MemoryMappedViewStream CreateViewStream ()
657 return CreateViewStream (0, 0);
660 public MemoryMappedViewStream CreateViewStream (long offset, long size)
662 return CreateViewStream (offset, size, MemoryMappedFileAccess.ReadWrite);
665 public MemoryMappedViewStream CreateViewStream (long offset, long size, MemoryMappedFileAccess access)
667 return new MemoryMappedViewStream (stream != null ? (int)stream.Handle : unix_fd, offset, size, access);
670 public MemoryMappedViewAccessor CreateViewAccessor ()
672 return CreateViewAccessor (0, 0);
675 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size)
677 return CreateViewAccessor (offset, size, MemoryMappedFileAccess.ReadWrite);
680 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size, MemoryMappedFileAccess access)
682 int file_handle = stream != null ? (int) stream.Handle : unix_fd;
684 return new MemoryMappedViewAccessor (file_handle, offset, size, access);
691 public void Dispose ()
696 protected virtual void Dispose (bool disposing)
700 if (keepOpen == false)
706 MemoryMapImpl.CloseFD (unix_fd);
714 public MemoryMappedFileSecurity GetAccessControl ()
716 throw new NotImplementedException ();
720 public void SetAccessControl (MemoryMappedFileSecurity memoryMappedFileSecurity)
722 throw new NotImplementedException ();
727 public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle {
729 throw new NotImplementedException ();