/mono-2.10.8/mcs/class/System.Core/System.IO.MemoryMappedFiles/MemoryMappedFile.cs

# · C# · 642 lines · 465 code · 122 blank · 55 comment · 82 complexity · 38f30920d0fa374bb709eca129fb739e MD5 · raw file

  1. //
  2. // MemoryMappedFile.cs
  3. //
  4. // Authors:
  5. // Zoltan Varga (vargaz@gmail.com)
  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 || MOBILE
  29. using System;
  30. using System.IO;
  31. using System.Collections.Generic;
  32. using Microsoft.Win32.SafeHandles;
  33. using System.Runtime.InteropServices;
  34. #if !MOBILE
  35. using Mono.Unix.Native;
  36. using Mono.Unix;
  37. #else
  38. using System.Runtime.CompilerServices;
  39. #endif
  40. namespace System.IO.MemoryMappedFiles
  41. {
  42. #if !MOBILE
  43. internal static class MemoryMapImpl {
  44. //
  45. // Turns the FileMode into the first half of open(2) flags
  46. //
  47. static OpenFlags ToUnixMode (FileMode mode)
  48. {
  49. switch (mode){
  50. case FileMode.CreateNew:
  51. return OpenFlags.O_CREAT | OpenFlags.O_EXCL;
  52. case FileMode.Create:
  53. return OpenFlags.O_CREAT | OpenFlags.O_TRUNC;
  54. case FileMode.OpenOrCreate:
  55. return OpenFlags.O_CREAT;
  56. case FileMode.Truncate:
  57. return OpenFlags.O_TRUNC;
  58. case FileMode.Append:
  59. return OpenFlags.O_APPEND;
  60. default:
  61. case FileMode.Open:
  62. return 0;
  63. }
  64. }
  65. //
  66. // Turns the MemoryMappedFileAccess into the second half of open(2) flags
  67. //
  68. static OpenFlags ToUnixMode (MemoryMappedFileAccess access)
  69. {
  70. switch (access){
  71. case MemoryMappedFileAccess.CopyOnWrite:
  72. case MemoryMappedFileAccess.ReadWriteExecute:
  73. case MemoryMappedFileAccess.ReadWrite:
  74. return OpenFlags.O_RDWR;
  75. case MemoryMappedFileAccess.Write:
  76. return OpenFlags.O_WRONLY;
  77. case MemoryMappedFileAccess.ReadExecute:
  78. case MemoryMappedFileAccess.Read:
  79. default:
  80. return OpenFlags.O_RDONLY;
  81. }
  82. }
  83. static MmapProts ToUnixProts (MemoryMappedFileAccess access)
  84. {
  85. switch (access){
  86. case MemoryMappedFileAccess.ReadWrite:
  87. return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
  88. case MemoryMappedFileAccess.Write:
  89. return MmapProts.PROT_WRITE;
  90. case MemoryMappedFileAccess.CopyOnWrite:
  91. return MmapProts.PROT_WRITE | MmapProts.PROT_READ;
  92. case MemoryMappedFileAccess.ReadExecute:
  93. return MmapProts.PROT_EXEC;
  94. case MemoryMappedFileAccess.ReadWriteExecute:
  95. return MmapProts.PROT_WRITE | MmapProts.PROT_READ | MmapProts.PROT_EXEC;
  96. case MemoryMappedFileAccess.Read:
  97. default:
  98. return MmapProts.PROT_READ;
  99. }
  100. }
  101. internal static int Open (string path, FileMode mode, long capacity, MemoryMappedFileAccess access)
  102. {
  103. if (MonoUtil.IsUnix){
  104. Stat buf;
  105. if (Syscall.stat (path, out buf) == -1)
  106. UnixMarshal.ThrowExceptionForLastError ();
  107. if ((capacity == 0 && buf.st_size == 0) || (capacity > buf.st_size))
  108. throw new ArgumentException ("capacity");
  109. int fd = Syscall.open (path, ToUnixMode (mode) | ToUnixMode (access), FilePermissions.DEFFILEMODE);
  110. if (fd == -1)
  111. UnixMarshal.ThrowExceptionForLastError ();
  112. return fd;
  113. } else
  114. throw new NotImplementedException ();
  115. }
  116. internal static void CloseFD (int fd) {
  117. Syscall.close (fd);
  118. }
  119. internal static void Flush (int fd) {
  120. if (MonoUtil.IsUnix)
  121. Syscall.fsync (fd);
  122. else
  123. throw new NotImplementedException ("Not implemented on Windows");
  124. }
  125. static int pagesize;
  126. internal static unsafe void Map (int file_handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr map_addr, out int offset_diff)
  127. {
  128. if (!MonoUtil.IsUnix)
  129. throw new NotImplementedException ("Not implemented on windows.");
  130. if (pagesize == 0)
  131. pagesize = Syscall.getpagesize ();
  132. Stat buf;
  133. Syscall.fstat (file_handle, out buf);
  134. long fsize = buf.st_size;
  135. if (size == 0 || size > fsize)
  136. size = fsize;
  137. // Align offset
  138. long real_offset = offset & ~(pagesize - 1);
  139. offset_diff = (int)(offset - real_offset);
  140. // FIXME: Need to determine the unix fd for the file, Handle is only
  141. // equal to it by accident
  142. //
  143. // The new API no longer uses FileStream everywhere, but exposes instead
  144. // the filename (with one exception), we could move this API to use
  145. // file descriptors instead of the FileStream plus its Handle.
  146. //
  147. map_addr = Syscall.mmap (IntPtr.Zero, (ulong) size,
  148. ToUnixProts (access),
  149. access == MemoryMappedFileAccess.CopyOnWrite ? MmapFlags.MAP_PRIVATE : MmapFlags.MAP_SHARED,
  150. file_handle, real_offset);
  151. if (map_addr == (IntPtr)(-1))
  152. throw new IOException ("mmap failed for fd#" + file_handle + "(" + offset + ", " + size + ")");
  153. }
  154. internal static bool Unmap (IntPtr map_addr, ulong map_size)
  155. {
  156. if (!MonoUtil.IsUnix)
  157. return false;
  158. return Syscall.munmap (map_addr, map_size) == 0;
  159. }
  160. static void ConfigureUnixFD (IntPtr handle, HandleInheritability h)
  161. {
  162. // TODO: Mono.Posix is lacking O_CLOEXEC definitions for fcntl.
  163. }
  164. [DllImport("kernel32.dll", SetLastError = true)]
  165. static extern bool SetHandleInformation (IntPtr hObject, int dwMask, int dwFlags);
  166. static void ConfigureWindowsFD (IntPtr handle, HandleInheritability h)
  167. {
  168. SetHandleInformation (handle, 1 /* FLAG_INHERIT */, h == HandleInheritability.None ? 0 : 1);
  169. }
  170. internal static void ConfigureFD (IntPtr handle, HandleInheritability inheritability)
  171. {
  172. if (MonoUtil.IsUnix)
  173. ConfigureUnixFD (handle, inheritability);
  174. else
  175. ConfigureWindowsFD (handle, inheritability);
  176. }
  177. }
  178. #else
  179. internal static class MemoryMapImpl {
  180. [DllImport ("libc")]
  181. static extern int fsync (int fd);
  182. [DllImport ("libc")]
  183. static extern int close (int fd);
  184. [DllImport ("libc")]
  185. static extern int fcntl (int fd, int cmd, int arg0);
  186. //XXX check if android off_t is 64bits or not. on iOS / darwin it is.
  187. [DllImport ("libc")]
  188. static extern IntPtr mmap (IntPtr addr, IntPtr len, int prot, int flags, int fd, long offset);
  189. [DllImport ("libc")]
  190. static extern int munmap (IntPtr addr, IntPtr size);
  191. [DllImport ("libc", SetLastError=true)]
  192. static extern int open (string path, int flags, int access);
  193. [DllImport ("libc")]
  194. static extern int getpagesize ();
  195. [MethodImplAttribute (MethodImplOptions.InternalCall)]
  196. static extern long mono_filesize_from_path (string str);
  197. [MethodImplAttribute (MethodImplOptions.InternalCall)]
  198. static extern long mono_filesize_from_fd (int fd);
  199. //Values valid on iOS/OSX and android ndk r6
  200. const int F_GETFD = 1;
  201. const int F_SETFD = 2;
  202. const int FD_CLOEXEC = 1;
  203. const int DEFFILEMODE = 0x666;
  204. const int O_RDONLY = 0x0;
  205. const int O_WRONLY = 0x1;
  206. const int O_RDWR = 0x2;
  207. const int PROT_READ = 0x1;
  208. const int PROT_WRITE = 0x2;
  209. const int PROT_EXEC = 0x4;
  210. const int MAP_PRIVATE = 0x2;
  211. const int MAP_SHARED = 0x1;
  212. const int EINVAL = 22;
  213. #if MONODROID
  214. const int O_CREAT = 0x040;
  215. const int O_TRUNC = 0x080;
  216. const int O_EXCL = 0x200;
  217. const int ENAMETOOLONG = 63;
  218. #else
  219. /* MONOTOUCH */
  220. const int O_CREAT = 0x0200;
  221. const int O_TRUNC = 0x0400;
  222. const int O_EXCL = 0x0800;
  223. const int ENAMETOOLONG = 36;
  224. #endif
  225. static int ToUnixMode (FileMode mode)
  226. {
  227. switch (mode) {
  228. case FileMode.CreateNew:
  229. return O_CREAT | O_EXCL;
  230. case FileMode.Create:
  231. return O_CREAT | O_TRUNC;
  232. case FileMode.OpenOrCreate:
  233. return O_CREAT;
  234. case FileMode.Truncate:
  235. return O_TRUNC;
  236. default:
  237. case FileMode.Open:
  238. return 0;
  239. }
  240. }
  241. //
  242. // Turns the MemoryMappedFileAccess into the second half of open(2) flags
  243. //
  244. static int ToUnixMode (MemoryMappedFileAccess access)
  245. {
  246. switch (access) {
  247. case MemoryMappedFileAccess.CopyOnWrite:
  248. case MemoryMappedFileAccess.ReadWriteExecute:
  249. case MemoryMappedFileAccess.ReadWrite:
  250. return O_RDWR;
  251. case MemoryMappedFileAccess.Write:
  252. return O_WRONLY;
  253. case MemoryMappedFileAccess.ReadExecute:
  254. case MemoryMappedFileAccess.Read:
  255. default:
  256. return O_RDONLY;
  257. }
  258. }
  259. static int ToUnixProts (MemoryMappedFileAccess access)
  260. {
  261. switch (access){
  262. case MemoryMappedFileAccess.ReadWrite:
  263. return PROT_WRITE | PROT_READ;
  264. case MemoryMappedFileAccess.Write:
  265. return PROT_WRITE;
  266. case MemoryMappedFileAccess.CopyOnWrite:
  267. return PROT_WRITE | PROT_READ;
  268. case MemoryMappedFileAccess.ReadExecute:
  269. return PROT_EXEC;
  270. case MemoryMappedFileAccess.ReadWriteExecute:
  271. return PROT_WRITE | PROT_READ | PROT_EXEC;
  272. case MemoryMappedFileAccess.Read:
  273. default:
  274. return PROT_READ;
  275. }
  276. }
  277. static void ThrowErrorFromErrno (int errno)
  278. {
  279. switch (errno) {
  280. case EINVAL: throw new ArgumentException ();
  281. case ENAMETOOLONG: throw new PathTooLongException ();
  282. default: throw new IOException ("Failed with errno " + errno);
  283. }
  284. }
  285. internal static int Open (string path, FileMode mode, long capacity, MemoryMappedFileAccess access)
  286. {
  287. long file_size = mono_filesize_from_path (path);
  288. if (file_size < 0)
  289. throw new FileNotFoundException (path);
  290. if ((capacity == 0 && file_size == 0) || (capacity > file_size))
  291. throw new ArgumentException ("capacity");
  292. int fd = open (path, ToUnixMode (mode) | ToUnixMode (access), DEFFILEMODE);
  293. if (fd == -1)
  294. ThrowErrorFromErrno (Marshal.GetLastWin32Error ());
  295. return fd;
  296. }
  297. internal static void CloseFD (int fd)
  298. {
  299. close (fd);
  300. }
  301. internal static void Flush (int fd)
  302. {
  303. fsync (fd);
  304. }
  305. internal static bool Unmap (IntPtr map_addr, ulong map_size)
  306. {
  307. return munmap (map_addr, (IntPtr)map_size) == 0;
  308. }
  309. static int pagesize;
  310. internal static unsafe void Map (int file_handle, long offset, ref long size, MemoryMappedFileAccess access, out IntPtr map_addr, out int offset_diff)
  311. {
  312. if (pagesize == 0)
  313. pagesize = getpagesize ();
  314. long fsize = mono_filesize_from_fd (file_handle);
  315. if (fsize < 0)
  316. throw new FileNotFoundException ();
  317. if (size == 0 || size > fsize)
  318. size = fsize;
  319. // Align offset
  320. long real_offset = offset & ~(pagesize - 1);
  321. offset_diff = (int)(offset - real_offset);
  322. map_addr = mmap (IntPtr.Zero, (IntPtr) size,
  323. ToUnixProts (access),
  324. access == MemoryMappedFileAccess.CopyOnWrite ? MAP_PRIVATE : MAP_SHARED,
  325. file_handle, real_offset);
  326. if (map_addr == (IntPtr)(-1))
  327. throw new IOException ("mmap failed for fd#" + file_handle + "(" + offset + ", " + size + ")");
  328. }
  329. internal static void ConfigureFD (IntPtr handle, HandleInheritability inheritability)
  330. {
  331. int fd = (int)handle;
  332. int flags = fcntl (fd, F_GETFD, 0);
  333. if (inheritability == HandleInheritability.None)
  334. flags &= ~FD_CLOEXEC;
  335. else
  336. flags |= FD_CLOEXEC;
  337. fcntl (fd, F_SETFD, flags);
  338. }
  339. }
  340. #endif
  341. public class MemoryMappedFile : IDisposable {
  342. MemoryMappedFileAccess fileAccess;
  343. string name;
  344. long fileCapacity;
  345. //
  346. // We allow the use of either the FileStream/keepOpen combo
  347. // or a Unix file descriptor. This way we avoid the dependency on
  348. // Mono's io-layer having the Unix file descriptors mapped to
  349. // the same io-layer handle
  350. //
  351. FileStream stream;
  352. bool keepOpen;
  353. int unix_fd;
  354. public static MemoryMappedFile CreateFromFile (string path)
  355. {
  356. return CreateFromFile (path, FileMode.Open, null, 0, MemoryMappedFileAccess.ReadWrite);
  357. }
  358. public static MemoryMappedFile CreateFromFile (string path, FileMode mode)
  359. {
  360. return CreateFromFile (path, mode, null, 0, MemoryMappedFileAccess.ReadWrite);
  361. }
  362. public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName)
  363. {
  364. return CreateFromFile (path, mode, mapName, 0, MemoryMappedFileAccess.ReadWrite);
  365. }
  366. public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity)
  367. {
  368. return CreateFromFile (path, mode, mapName, capacity, MemoryMappedFileAccess.ReadWrite);
  369. }
  370. public static MemoryMappedFile CreateFromFile (string path, FileMode mode, string mapName, long capacity, MemoryMappedFileAccess access)
  371. {
  372. if (path == null)
  373. throw new ArgumentNullException ("path");
  374. if (path.Length == 0)
  375. throw new ArgumentException ("path");
  376. if (mapName != null && mapName.Length == 0)
  377. throw new ArgumentException ("mapName");
  378. if (mode == FileMode.Append)
  379. throw new ArgumentException ("mode");
  380. int fd = MemoryMapImpl.Open (path, mode, capacity, access);
  381. return new MemoryMappedFile () {
  382. unix_fd = fd,
  383. fileAccess = access,
  384. name = mapName,
  385. fileCapacity = capacity
  386. };
  387. }
  388. [MonoLimitation ("memoryMappedFileSecurity is currently ignored")]
  389. public static MemoryMappedFile CreateFromFile (FileStream fileStream, string mapName, long capacity, MemoryMappedFileAccess access,
  390. MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability inheritability,
  391. bool leaveOpen)
  392. {
  393. if (fileStream == null)
  394. throw new ArgumentNullException ("fileStream");
  395. if (mapName != null && mapName.Length == 0)
  396. throw new ArgumentException ("mapName");
  397. if ((capacity == 0 && fileStream.Length == 0) || (capacity > fileStream.Length))
  398. throw new ArgumentException ("capacity");
  399. MemoryMapImpl.ConfigureFD (fileStream.Handle, inheritability);
  400. return new MemoryMappedFile () {
  401. stream = fileStream,
  402. fileAccess = access,
  403. name = mapName,
  404. fileCapacity = capacity,
  405. keepOpen = leaveOpen
  406. };
  407. }
  408. [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
  409. public static MemoryMappedFile CreateNew (string mapName, long capacity)
  410. {
  411. return CreateNew (mapName, capacity, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
  412. }
  413. [MonoLimitation ("CreateNew requires that mapName be a file name on Unix")]
  414. public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access)
  415. {
  416. return CreateNew (mapName, capacity, access, MemoryMappedFileOptions.DelayAllocatePages, null, 0);
  417. }
  418. [MonoLimitation ("CreateNew requires that mapName be a file name on Unix; options and memoryMappedFileSecurity are ignored")]
  419. public static MemoryMappedFile CreateNew (string mapName, long capacity, MemoryMappedFileAccess access,
  420. MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity,
  421. HandleInheritability handleInheritability)
  422. {
  423. return CreateFromFile (mapName, FileMode.CreateNew, mapName, capacity, access);
  424. }
  425. [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
  426. public static MemoryMappedFile CreateOrOpen (string mapName, long capacity)
  427. {
  428. return CreateOrOpen (mapName, capacity, MemoryMappedFileAccess.ReadWrite);
  429. }
  430. [MonoLimitation ("CreateOrOpen requires that mapName be a file name on Unix")]
  431. public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access)
  432. {
  433. return CreateFromFile (mapName, FileMode.OpenOrCreate, mapName, capacity, access);
  434. }
  435. [MonoTODO]
  436. public static MemoryMappedFile CreateOrOpen (string mapName, long capacity, MemoryMappedFileAccess access, MemoryMappedFileOptions options, MemoryMappedFileSecurity memoryMappedFileSecurity, HandleInheritability handleInheritability)
  437. {
  438. throw new NotImplementedException ();
  439. }
  440. [MonoTODO]
  441. public static MemoryMappedFile OpenExisting (string mapName)
  442. {
  443. throw new NotImplementedException ();
  444. }
  445. [MonoTODO]
  446. public static MemoryMappedFile OpenExisting (string mapName, MemoryMappedFileRights desiredAccessRights)
  447. {
  448. throw new NotImplementedException ();
  449. }
  450. [MonoTODO]
  451. public static MemoryMappedFile OpenExisting (string mapName, MemoryMappedFileRights desiredAccessRights, HandleInheritability inheritability)
  452. {
  453. throw new NotImplementedException ();
  454. }
  455. public MemoryMappedViewStream CreateViewStream ()
  456. {
  457. return CreateViewStream (0, 0);
  458. }
  459. public MemoryMappedViewStream CreateViewStream (long offset, long size)
  460. {
  461. return CreateViewStream (offset, size, MemoryMappedFileAccess.ReadWrite);
  462. }
  463. public MemoryMappedViewStream CreateViewStream (long offset, long size, MemoryMappedFileAccess access)
  464. {
  465. return new MemoryMappedViewStream (stream != null ? (int)stream.Handle : unix_fd, offset, size, access);
  466. }
  467. public MemoryMappedViewAccessor CreateViewAccessor ()
  468. {
  469. return CreateViewAccessor (0, 0);
  470. }
  471. public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size)
  472. {
  473. return CreateViewAccessor (offset, size, MemoryMappedFileAccess.ReadWrite);
  474. }
  475. public MemoryMappedViewAccessor CreateViewAccessor (long offset, long size, MemoryMappedFileAccess access)
  476. {
  477. int file_handle = stream != null ? (int) stream.Handle : unix_fd;
  478. return new MemoryMappedViewAccessor (file_handle, offset, size, access);
  479. }
  480. MemoryMappedFile ()
  481. {
  482. }
  483. public void Dispose ()
  484. {
  485. Dispose (true);
  486. }
  487. protected virtual void Dispose (bool disposing)
  488. {
  489. if (disposing){
  490. if (stream != null){
  491. if (keepOpen == false)
  492. stream.Close ();
  493. unix_fd = -1;
  494. stream = null;
  495. }
  496. if (unix_fd != -1) {
  497. MemoryMapImpl.CloseFD (unix_fd);
  498. unix_fd = -1;
  499. }
  500. }
  501. }
  502. [MonoTODO]
  503. public MemoryMappedFileSecurity GetAccessControl ()
  504. {
  505. throw new NotImplementedException ();
  506. }
  507. [MonoTODO]
  508. public void SetAccessControl (MemoryMappedFileSecurity memoryMappedFileSecurity)
  509. {
  510. throw new NotImplementedException ();
  511. }
  512. [MonoTODO]
  513. public SafeMemoryMappedFileHandle SafeMemoryMappedFileHandle {
  514. get {
  515. throw new NotImplementedException ();
  516. }
  517. }
  518. }
  519. }
  520. #endif