context: //
  // MemoryMappedFile.cs
  //
  // Authors:
  //	Zoltan Varga (vargaz@gmail.com)
  //
  // Copyright (C) 2009, Novell, Inc (http://www.novell.com)
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  //
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  
  #if NET_4_0
  
  using System;
  using System.IO;
  using System.Collections.Generic;
  using Microsoft.Win32.SafeHandles;
  using System.Runtime.InteropServices;
  
  
  #if !MOBILE
  using Mono.Unix.Native;
  using Mono.Unix;
  #else
  using System.Runtime.CompilerServices;
  #endif
  
  namespace System.IO.MemoryMappedFiles
  {
  #if !MOBILE
  	internal static class MemoryMapImpl {
  		//
  		// Turns the FileMode into the first half of open(2) flags
  		//
  		static OpenFlags ToUnixMode (FileMode mode)
  		{
  			switch (mode){
  			case FileMode.CreateNew:
  				return OpenFlags.O_CREAT | OpenFlags.O_EXCL;
  				
  			case FileMode.Create:
  				return OpenFlags.O_CREAT | OpenFlags.O_TRUNC;
  				
  			case FileMode.OpenOrCreate:
  				return OpenFlags.O_CREAT;
  				
  			case FileMode.Truncate:
  				return OpenFlags.O_TRUNC;
  				
  			case FileMode.Append:
  				return OpenFlags.O_APPEND;
  			default:
  			case FileMode.Open:
  				return 0;
  			}
  		}
  
  		//
  		// Turns the MemoryMappedFileAccess into the second half of open(2) flags
  		//
  		static OpenFlags ToUnixMode (MemoryMappedFileAccess access)
  		{
  			switch (access){
  			case MemoryMappedFileAccess.CopyOnWrite:
  			case MemoryMappedFileAccess.ReadWriteExecute:
  			case MemoryMappedFileAccess.ReadWrite:
  				return OpenFlags.O_RDWR;
  				
  			case MemoryMappedFileAccess.Write:
  				return OpenFlags.O_WRONLY;
  
  			case MemoryMappedFileAccess.ReadExecute:
  			case MemoryMappedFileAccess.Read:
  			default:
  				return OpenFlags.O_RDONLY;
  			}
  		}
  
  		static MmapProts ToUnixProts (MemoryMappedFileAccess access)
  		{
  			switch (access){
  			case MemoryMappedFileAccess.ReadWrite:
  				return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
  				
  			case MemoryMappedFileAccess.Write:
  				return MmapProts.PROT_WRITE;
  				
  			case MemoryMappedFileAccess.CopyOnWrite:
  				return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
  				
  			case MemoryMappedFileAccess.ReadExecute:
  				return MmapProts.PROT_EXEC;
  				
  			case MemoryMappedFileAccess.ReadWriteExecute:
  				return MmapProts.PROT_WRITE | MmapProts.PROT_READ | MmapProts.PROT_EXEC;
  				
  			case MemoryMappedFileAccess.Read:
  			default:
  				return MmapProts.PROT_READ;
  			}
  		}
  
  		internal static int Open (string path, FileMode mode, long capacity, MemoryMappedFileAccess access)
  		{
  			if (MonoUtil.IsUnix){
  				Stat buf;
  				if (Syscall.stat (path, out buf) == -1)
  					UnixMarshal.ThrowExceptionForLastError ();
  
  				if (capacity > buf.st_size)
  					throw new ArgumentException ("capacity");
  
  				int fd = Syscall.open (path, ToUnixMode (mode) | ToUnixMode (access), FilePermissions.DEFFILEMODE);
  
  				if (fd == -1)
  					UnixMarshal.ThrowExceptionForLastError ();
  				return fd;
  			} else
  				throw new NotImplementedException ();
  		}
  		
  		internal static void CloseFD (int fd) {
  			Syscall.close (fd);
  		}
  
  		internal static void Flush (int fd) {
  			if (MonoUtil.IsUnix)
  				Syscall.fsync (fd);
  			else
  				throw new NotImplementedException ("Not implemented on Windows");
  			
  		}
  
  		static int pagesize;
  
  		internal static unsafe void Map (int file_handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr map_addr, out int offset_diff)
  		{
  			if (!MonoUtil.IsUnix)
  				throw new NotImplementedException ("Not implemented on windows.");
  
  			if (pagesize == 0)
  				pagesize = Syscall.getpagesize ();
  
  			Stat buf;
  			Syscall.fstat (file_handle, out buf);
  			long fsize = buf.st_size;
  
  			if (size == 0 || size > fsize)
  				size = fsize;
  			
  			// Align offset
  			long real_offset = offset & ~(pagesize - 1);
  
  			offset_diff = (int)(offset - real_offset);
  
  			// FIXME: Need to determine the unix fd for the file, Handle is only
  			// equal to it by accident
  			//
  			// The new API no longer uses FileStream everywhere, but exposes instead
  			// the filename (with one exception), we could move this API to use
  			// file descriptors instead of the FileStream plus its Handle.
  			//
  			map_addr = Syscall.mmap (IntPtr.Zero, (ulong) size,
  						 ToUnixProts (access),
  						 access == MemoryMappedFileAccess.CopyOnWrite ? MmapFlags.MAP_PRIVATE : MmapFlags.MAP_SHARED,
  						 file_handle, real_offset);
  
  			if (map_addr == (IntPtr)(-1))
  				throw new IOException ("mmap failed for fd#" + file_handle + "(" + offset + ", " + size + ")");
  		}
  
  		internal static bool Unmap (IntPtr map_addr, ulong map_size)
  		{
  			if (!MonoUtil.IsUnix)
  				return false;
  			return Syscall.munmap (map_addr, map_size) == 0;
  		}
  
  		static void ConfigureUnixFD (IntPtr handle, HandleInheritability h)
  		{
  			// TODO: Mono.Posix is lacking O_CLOEXEC definitions for fcntl.
  		}
  
  
  		[DllImport("kernel32", SetLastError = true)]
  		static extern bool SetHandleInformation (IntPtr hObject, int dwMask, int dwFlags);
  		static void ConfigureWindowsFD (IntPtr handle, HandleInheritability h)
  		{
  			SetHandleInformation (handle, 1 /* FLAG_INHERIT */, h == HandleInheritability.None ? 0 : 1);
  		}
  		
  		internal static void ConfigureFD (IntPtr handle, HandleInheritability inheritability)
  		{
  			if (MonoUtil.IsUnix)
  
gt: 				ConfigureUnixFD (handle, inheritability);