MemoryMappedFile.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. //
  2. // MemoryMappedFile.cs
  3. //
  4. // Authors:
  5. // Zoltan Varga ([email protected])
  6. //
  7. // Copyright (C) 2009, Novell, Inc (http://www.novell.com)
  8. //
  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:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  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.
  27. //
  28. #if NET_4_0
  29. using System;
  30. using System.IO;
  31. using System.Collections.Generic;
  32. using Microsoft.Win32.SafeHandles;
  33. using Mono.Unix.Native;
  34. using Mono.Unix;
  35. using System.Runtime.InteropServices;
  36. namespace System.IO.MemoryMappedFiles
  37. {
  38. public class MemoryMappedFile : IDisposable {
  39. MemoryMappedFileAccess fileAccess;
  40. string name;
  41. long fileCapacity;
  42. //
  43. // We allow the use of either the FileStream/keepOpen combo
  44. // or a Unix file descriptor. This way we avoid the dependency on
  45. // Mono's io-layer having the Unix file descriptors mapped to
  46. // the same io-layer handle
  47. //
  48. FileStream stream;
  49. bool keepOpen;
  50. int unix_fd;
  51. public static MemoryMappedFile CreateFromFile (FileStream fileStream)
  52. {
  53. if (fileStream == null)
  54. throw new ArgumentNullException ("fileStream");
  55. return new MemoryMappedFile () {
  56. stream = fileStream,
  57. fileAccess = MemoryMappedFileAccess.ReadWrite
  58. };
  59. }
  60. public static MemoryMappedFile CreateFromFile (string path)
  61. {
  62. return CreateFromFile (path, FileMode.Open, null, 0, MemoryMappedFileAccess.ReadWrite);
  63. }
  64. public static MemoryMappedFile CreateFromFile (string path, FileMode mode)
  65. {
  66. return CreateFromFile (path, mode, null, 0, MemoryMappedFileAccess.ReadWrite);
  67. }
  68. public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName)
  69. {
  70. return CreateFromFile (path, mode, mapName, 0, MemoryMappedFileAccess.ReadWrite);
  71. }
  72. public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity)
  73. {
  74. return CreateFromFile (path, mode, mapName, capacity, MemoryMappedFileAccess.ReadWrite);
  75. }
  76. //
  77. // Turns the FileMode into the first half of open(2) flags
  78. //
  79. static OpenFlags ToUnixMode (FileMode mode)
  80. {
  81. switch (mode){
  82. case FileMode.CreateNew:
  83. return OpenFlags.O_CREAT | OpenFlags.O_EXCL;
  84. case FileMode.Create:
  85. return OpenFlags.O_CREAT | OpenFlags.O_TRUNC;
  86. case FileMode.OpenOrCreate:
  87. return OpenFlags.O_CREAT;
  88. case FileMode.Truncate:
  89. return OpenFlags.O_TRUNC;
  90. case FileMode.Append:
  91. return OpenFlags.O_APPEND;
  92. default:
  93. case FileMode.Open:
  94. return 0;
  95. }
  96. }
  97. //
  98. // Turns the MemoryMappedFileAccess into the second half of open(2) flags
  99. //
  100. static OpenFlags ToUnixMode (MemoryMappedFileAccess access)
  101. {
  102. switch (access){
  103. case MemoryMappedFileAccess.CopyOnWrite:
  104. case MemoryMappedFileAccess.ReadWriteExecute:
  105. case MemoryMappedFileAccess.ReadWrite:
  106. return OpenFlags.O_RDWR;
  107. case MemoryMappedFileAccess.Write:
  108. return OpenFlags.O_WRONLY;
  109. case MemoryMappedFileAccess.ReadExecute:
  110. case MemoryMappedFileAccess.Read:
  111. default:
  112. return OpenFlags.O_RDONLY;
  113. }
  114. }
  115. public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity, MemoryMappedFileAccess access)
  116. {
  117. if (path == null)
  118. throw new ArgumentNullException ("path");
  119. if (path.Length == 0)
  120. throw new ArgumentException ("path");
  121. if (mapName != null && mapName.Length == 0)
  122. throw new ArgumentException ("mapName");
  123. int fd;
  124. if (MonoUtil.IsUnix){
  125. Stat buf;
  126. if (Syscall.stat (path, out buf) == -1)
  127. UnixMarshal.ThrowExceptionForLastError ();
  128. if ((capacity == 0 && buf.st_size == 0) || (capacity > buf.st_size))
  129. throw new ArgumentException ("capacity");
  130. fd = Syscall.open (path, ToUnixMode (mode) | ToUnixMode (access), FilePermissions.DEFFILEMODE);
  131. if (fd == -1)
  132. UnixMarshal.ThrowExceptionForLastError ();
  133. } else
  134. throw new NotImplementedException ();
  135. return new MemoryMappedFile () {
  136. unix_fd = fd,
  137. fileAccess = access,
  138. name = mapName,
  139. fileCapacity = capacity
  140. };
  141. }
  142. public static void ConfigureUnixFD (IntPtr handle, HandleInheritability h)
  143. {
  144. // TODO: Mono.Posix is lacking O_CLOEXEC definitions for fcntl.
  145. }
  146. [DllImport("kernel32.dll", SetLastError = true)]
  147. static extern bool SetHandleInformation (IntPtr hObject, int dwMask, int dwFlags);
  148. public static void ConfigureWindowsFD (IntPtr handle, HandleInheritability h)
  149. {
  150. SetHandleInformation (handle, 1 /* FLAG_INHERIT */, h == HandleInheritability.None ? 0 : 1);
  151. }
  152. [MonoLimitation ("memoryMappedFileSecurity is currently ignored")]
  153. public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
  154. MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability,
  155. bool leaveOpen)
  156. {
  157. if (fileStream == null)
  158. throw new ArgumentNullException ("fileStream");
  159. if (mapName != null && mapName.Length == 0)
  160. throw new ArgumentException ("mapName");
  161. if ((capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
  162. throw new ArgumentException ("capacity");
  163. if (MonoUtil.IsUnix)
  164. ConfigureUnixFD (fileStream.Handle, inheritability);
  165. else
  166. ConfigureWindowsFD (fileStream.Handle, inheritability);
  167. return new MemoryMappedFile () {
  168. stream = fileStream,
  169. fileAccess = access,
  170. name = mapName,
  171. fileCapacity = capacity,
  172. keepOpen = leaveOpen
  173. };
  174. }
  175. [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
  176. public static MemoryMappedFile CreateNew (string mapName, long capacity)
  177. {
  178. return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
  179. }
  180. [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
  181. public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access)
  182. {
  183. return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
  184. }
  185. [MonoLimitation ("CreateNew requires that mapName be a file name on Unix; options and memoryMappedFileSecurity are ignored")]
  186. public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access,
  187. MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity,
  188. HandleInheritability handleInheritability)
  189. {
  190. return CreateFromFile (mapName, FileMode.CreateNew, mapName, capacity, access);
  191. }
  192. [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
  193. public static MemoryMappedFile CreateOrOpen (string mapName, long capacity)
  194. {
  195. return CreateOrOpen (mapName, capacity, MemoryMappedFileAccess.ReadWrite);
  196. }
  197. [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
  198. public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access)
  199. {
  200. return CreateFromFile (mapName, FileMode.OpenOrCreate, mapName, capacity, access);
  201. }
  202. /*
  203. [MonoTODO]
  204. public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability handleInheritability) {
  205. throw new NotImplementedException ();
  206. }
  207. */
  208. public MemoryMappedViewStream CreateViewStream ()
  209. {
  210. return CreateViewStream (0, 0);
  211. }
  212. public MemoryMappedViewStream CreateViewStream (long offset, long size)
  213. {
  214. return CreateViewStream (offset, size, MemoryMappedFileAccess.ReadWrite);
  215. }
  216. public MemoryMappedViewStream CreateViewStream (long offset, long size, MemoryMappedFileAccess access)
  217. {
  218. return new MemoryMappedViewStream (stream != null ? (int)stream.Handle : unix_fd, offset, size, access);
  219. }
  220. public MemoryMappedViewAccessor CreateViewAccessor ()
  221. {
  222. return CreateViewAccessor (0, 0);
  223. }
  224. public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size)
  225. {
  226. return CreateViewAccessor (offset, size, MemoryMappedFileAccess.ReadWrite);
  227. }
  228. public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size, MemoryMappedFileAccess access)
  229. {
  230. int file_handle = stream != null ? (int) stream.Handle : unix_fd;
  231. return new MemoryMappedViewAccessor (file_handle, offset, size, access);
  232. }
  233. MemoryMappedFile ()
  234. {
  235. }
  236. public void Dispose ()
  237. {
  238. Dispose (true);
  239. }
  240. protected virtual void Dispose (bool disposing)
  241. {
  242. if (disposing){
  243. if (stream != null){
  244. if (keepOpen == false)
  245. stream.Close ();
  246. unix_fd = -1;
  247. }
  248. if (unix_fd != -1)
  249. Syscall.close (unix_fd);
  250. unix_fd = -1;
  251. stream = null;
  252. }
  253. }
  254. [MonoTODO]
  255. public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle {
  256. get {
  257. throw new NotImplementedException ();
  258. }
  259. }
  260. static int pagesize;
  261. static MmapProts ToUnixProts (MemoryMappedFileAccess access)
  262. {
  263. MmapProts prots;
  264. switch (access){
  265. case MemoryMappedFileAccess.ReadWrite:
  266. return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
  267. break;
  268. case MemoryMappedFileAccess.Write:
  269. return MmapProts.PROT_WRITE;
  270. case MemoryMappedFileAccess.CopyOnWrite:
  271. return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
  272. case MemoryMappedFileAccess.ReadExecute:
  273. return MmapProts.PROT_EXEC;
  274. case MemoryMappedFileAccess.ReadWriteExecute:
  275. return MmapProts.PROT_WRITE | MmapProts.PROT_READ | MmapProts.PROT_EXEC;
  276. case MemoryMappedFileAccess.Read:
  277. default:
  278. return MmapProts.PROT_READ;
  279. }
  280. }
  281. internal static unsafe void MapPosix (int file_handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr map_addr, out int offset_diff)
  282. {
  283. if (pagesize == 0)
  284. pagesize = Syscall.getpagesize ();
  285. Stat buf;
  286. Syscall.fstat (file_handle, out buf);
  287. long fsize = buf.st_size;
  288. if (size == 0 || size > fsize)
  289. size = fsize;
  290. // Align offset
  291. long real_offset = offset & ~(pagesize - 1);
  292. offset_diff = (int)(offset - real_offset);
  293. // FIXME: Need to determine the unix fd for the file, Handle is only
  294. // equal to it by accident
  295. //
  296. // The new API no longer uses FileStream everywhere, but exposes instead
  297. // the filename (with one exception), we could move this API to use
  298. // file descriptors instead of the FileStream plus its Handle.
  299. //
  300. map_addr = Syscall.mmap (IntPtr.Zero, (ulong) size,
  301. ToUnixProts (access),
  302. access == MemoryMappedFileAccess.CopyOnWrite ? MmapFlags.MAP_PRIVATE : MmapFlags.MAP_SHARED,
  303. file_handle, real_offset);
  304. if (map_addr == (IntPtr)(-1))
  305. throw new IOException ("mmap failed for fd#" + file_handle + "(" + offset + ", " + size + ")");
  306. }
  307. internal static bool UnmapPosix (IntPtr map_addr, ulong map_size)
  308. {
  309. return Syscall.munmap (map_addr, map_size) == 0;
  310. }
  311. }
  312. }
  313. #endif