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 Mono.Unix.Native;
37 using System.Runtime.InteropServices;
39 namespace System.IO.MemoryMappedFiles
41 public class MemoryMappedFile : IDisposable {
42 MemoryMappedFileAccess fileAccess;
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
56 public static MemoryMappedFile CreateFromFile (FileStream fileStream)
58 if (fileStream == null)
59 throw new ArgumentNullException ("fileStream");
61 return new MemoryMappedFile () {
63 fileAccess = MemoryMappedFileAccess.ReadWrite
67 public static MemoryMappedFile CreateFromFile (string path)
69 return CreateFromFile (path, FileMode.Open, null, 0, MemoryMappedFileAccess.ReadWrite);
72 public static MemoryMappedFile CreateFromFile (string path, FileMode mode)
74 return CreateFromFile (path, mode, null, 0, MemoryMappedFileAccess.ReadWrite);
77 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName)
79 return CreateFromFile (path, mode, mapName, 0, MemoryMappedFileAccess.ReadWrite);
82 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity)
84 return CreateFromFile (path, mode, mapName, capacity, MemoryMappedFileAccess.ReadWrite);
88 // Turns the FileMode into the first half of open(2) flags
90 static OpenFlags ToUnixMode (FileMode mode)
93 case FileMode.CreateNew:
94 return OpenFlags.O_CREAT | OpenFlags.O_EXCL;
97 return OpenFlags.O_CREAT | OpenFlags.O_TRUNC;
99 case FileMode.OpenOrCreate:
100 return OpenFlags.O_CREAT;
102 case FileMode.Truncate:
103 return OpenFlags.O_TRUNC;
105 case FileMode.Append:
106 return OpenFlags.O_APPEND;
114 // Turns the MemoryMappedFileAccess into the second half of open(2) flags
116 static OpenFlags ToUnixMode (MemoryMappedFileAccess access)
119 case MemoryMappedFileAccess.CopyOnWrite:
120 case MemoryMappedFileAccess.ReadWriteExecute:
121 case MemoryMappedFileAccess.ReadWrite:
122 return OpenFlags.O_RDWR;
124 case MemoryMappedFileAccess.Write:
125 return OpenFlags.O_WRONLY;
127 case MemoryMappedFileAccess.ReadExecute:
128 case MemoryMappedFileAccess.Read:
130 return OpenFlags.O_RDONLY;
134 public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity, MemoryMappedFileAccess access)
137 throw new ArgumentNullException ("path");
138 if (path.Length == 0)
139 throw new ArgumentException ("path");
140 if (mapName != null && mapName.Length == 0)
141 throw new ArgumentException ("mapName");
144 if (MonoUtil.IsUnix){
146 if (Syscall.stat (path, out buf) == -1)
147 UnixMarshal.ThrowExceptionForLastError ();
149 if ((capacity == 0 && buf.st_size == 0) || (capacity > buf.st_size))
150 throw new ArgumentException ("capacity");
152 fd = Syscall.open (path, ToUnixMode (mode) | ToUnixMode (access), FilePermissions.DEFFILEMODE);
155 UnixMarshal.ThrowExceptionForLastError ();
157 throw new NotImplementedException ();
159 return new MemoryMappedFile () {
163 fileCapacity = capacity
167 static void ConfigureUnixFD (IntPtr handle, HandleInheritability h)
169 // TODO: Mono.Posix is lacking O_CLOEXEC definitions for fcntl.
173 [DllImport("kernel32.dll", SetLastError = true)]
174 static extern bool SetHandleInformation (IntPtr hObject, int dwMask, int dwFlags);
175 static void ConfigureWindowsFD (IntPtr handle, HandleInheritability h)
177 SetHandleInformation (handle, 1 /* FLAG_INHERIT */, h == HandleInheritability.None ? 0 : 1);
180 [MonoLimitation ("memoryMappedFileSecurity is currently ignored")]
181 public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
182 MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability,
185 if (fileStream == null)
186 throw new ArgumentNullException ("fileStream");
187 if (mapName != null && mapName.Length == 0)
188 throw new ArgumentException ("mapName");
189 if ((capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
190 throw new ArgumentException ("capacity");
193 ConfigureUnixFD (fileStream.Handle, inheritability);
195 ConfigureWindowsFD (fileStream.Handle, inheritability);
197 return new MemoryMappedFile () {
201 fileCapacity = capacity,
206 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
207 public static MemoryMappedFile CreateNew (string mapName, long capacity)
209 return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
212 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
213 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access)
215 return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
218 [MonoLimitation ("CreateNew requires that mapName be a file name on Unix; options and memoryMappedFileSecurity are ignored")]
219 public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access,
220 MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity,
221 HandleInheritability handleInheritability)
223 return CreateFromFile (mapName, FileMode.CreateNew, mapName, capacity, access);
226 [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
227 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity)
229 return CreateOrOpen (mapName, capacity, MemoryMappedFileAccess.ReadWrite);
232 [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
233 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access)
235 return CreateFromFile (mapName, FileMode.OpenOrCreate, mapName, capacity, access);
240 public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability handleInheritability) {
241 throw new NotImplementedException ();
245 public MemoryMappedViewStream CreateViewStream ()
247 return CreateViewStream (0, 0);
250 public MemoryMappedViewStream CreateViewStream (long offset, long size)
252 return CreateViewStream (offset, size, MemoryMappedFileAccess.ReadWrite);
255 public MemoryMappedViewStream CreateViewStream (long offset, long size, MemoryMappedFileAccess access)
257 return new MemoryMappedViewStream (stream != null ? (int)stream.Handle : unix_fd, offset, size, access);
260 public MemoryMappedViewAccessor CreateViewAccessor ()
262 return CreateViewAccessor (0, 0);
265 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size)
267 return CreateViewAccessor (offset, size, MemoryMappedFileAccess.ReadWrite);
270 public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size, MemoryMappedFileAccess access)
272 int file_handle = stream != null ? (int) stream.Handle : unix_fd;
274 return new MemoryMappedViewAccessor (file_handle, offset, size, access);
281 public void Dispose ()
286 protected virtual void Dispose (bool disposing)
290 if (keepOpen == false)
295 Syscall.close (unix_fd);
302 public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle {
304 throw new NotImplementedException ();
310 static MmapProts ToUnixProts (MemoryMappedFileAccess access)
315 case MemoryMappedFileAccess.ReadWrite:
316 return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
319 case MemoryMappedFileAccess.Write:
320 return MmapProts.PROT_WRITE;
322 case MemoryMappedFileAccess.CopyOnWrite:
323 return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
325 case MemoryMappedFileAccess.ReadExecute:
326 return MmapProts.PROT_EXEC;
328 case MemoryMappedFileAccess.ReadWriteExecute:
329 return MmapProts.PROT_WRITE | MmapProts.PROT_READ | MmapProts.PROT_EXEC;
331 case MemoryMappedFileAccess.Read:
333 return MmapProts.PROT_READ;
337 internal static unsafe void MapPosix (int file_handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr map_addr, out int offset_diff)
340 pagesize = Syscall.getpagesize ();
343 Syscall.fstat (file_handle, out buf);
344 long fsize = buf.st_size;
346 if (size == 0 || size > fsize)
350 long real_offset = offset & ~(pagesize - 1);
352 offset_diff = (int)(offset - real_offset);
354 // FIXME: Need to determine the unix fd for the file, Handle is only
355 // equal to it by accident
357 // The new API no longer uses FileStream everywhere, but exposes instead
358 // the filename (with one exception), we could move this API to use
359 // file descriptors instead of the FileStream plus its Handle.
361 map_addr = Syscall.mmap (IntPtr.Zero, (ulong) size,
362 ToUnixProts (access),
363 access == MemoryMappedFileAccess.CopyOnWrite ? MmapFlags.MAP_PRIVATE : MmapFlags.MAP_SHARED,
364 file_handle, real_offset);
366 if (map_addr == (IntPtr)(-1))
367 throw new IOException ("mmap failed for fd#" + file_handle + "(" + offset + ", " + size + ")");
370 internal static bool UnmapPosix (IntPtr map_addr, ulong map_size)
372 return Syscall.munmap (map_addr, map_size) == 0;