file-mmap-posix.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. /**
  2. * \file
  3. * File mmap internal calls
  4. *
  5. * Author:
  6. * Rodrigo Kumpera
  7. *
  8. * Copyright 2014 Xamarin Inc (http://www.xamarin.com)
  9. * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  10. */
  11. #include <config.h>
  12. #ifndef HOST_WIN32
  13. #include <glib.h>
  14. #include <string.h>
  15. #include <errno.h>
  16. #ifdef HAVE_UNISTD_H
  17. #include <unistd.h>
  18. #endif
  19. #ifdef HAVE_SYS_STAT_H
  20. #include <sys/stat.h>
  21. #endif
  22. #ifdef HAVE_SYS_TYPES_H
  23. #include <sys/types.h>
  24. #endif
  25. #if HAVE_SYS_MMAN_H
  26. #include <sys/mman.h>
  27. #endif
  28. #include <fcntl.h>
  29. #include <mono/metadata/object.h>
  30. #include <mono/metadata/w32file.h>
  31. #include <mono/metadata/file-mmap.h>
  32. #include <mono/utils/atomic.h>
  33. #include <mono/utils/mono-memory-model.h>
  34. #include <mono/utils/mono-mmap.h>
  35. #include <mono/utils/mono-coop-mutex.h>
  36. #include <mono/utils/mono-threads.h>
  37. typedef struct {
  38. int kind;
  39. int ref_count;
  40. size_t capacity;
  41. char *name;
  42. int fd;
  43. } MmapHandle;
  44. typedef struct {
  45. void *address;
  46. void *free_handle;
  47. size_t length;
  48. } MmapInstance;
  49. enum {
  50. BAD_CAPACITY_FOR_FILE_BACKED = 1,
  51. CAPACITY_SMALLER_THAN_FILE_SIZE,
  52. FILE_NOT_FOUND,
  53. FILE_ALREADY_EXISTS,
  54. PATH_TOO_LONG,
  55. COULD_NOT_OPEN,
  56. CAPACITY_MUST_BE_POSITIVE,
  57. INVALID_FILE_MODE,
  58. COULD_NOT_MAP_MEMORY,
  59. ACCESS_DENIED,
  60. CAPACITY_LARGER_THAN_LOGICAL_ADDRESS_SPACE
  61. };
  62. enum {
  63. FILE_MODE_CREATE_NEW = 1,
  64. FILE_MODE_CREATE = 2,
  65. FILE_MODE_OPEN = 3,
  66. FILE_MODE_OPEN_OR_CREATE = 4,
  67. FILE_MODE_TRUNCATE = 5,
  68. FILE_MODE_APPEND = 6,
  69. };
  70. enum {
  71. MMAP_FILE_ACCESS_READ_WRITE = 0,
  72. MMAP_FILE_ACCESS_READ = 1,
  73. MMAP_FILE_ACCESS_WRITE = 2,
  74. MMAP_FILE_ACCESS_COPY_ON_WRITE = 3,
  75. MMAP_FILE_ACCESS_READ_EXECUTE = 4,
  76. MMAP_FILE_ACCESS_READ_WRITE_EXECUTE = 5,
  77. };
  78. #ifdef DEFFILEMODE
  79. #define DEFAULT_FILEMODE DEFFILEMODE
  80. #else
  81. #define DEFAULT_FILEMODE 0666
  82. #endif
  83. static int mmap_init_state;
  84. static MonoCoopMutex named_regions_mutex;
  85. static GHashTable *named_regions;
  86. static gint64
  87. align_up_to_page_size (gint64 size)
  88. {
  89. gint64 page_size = mono_pagesize ();
  90. return (size + page_size - 1) & ~(page_size - 1);
  91. }
  92. static gint64
  93. align_down_to_page_size (gint64 size)
  94. {
  95. gint64 page_size = mono_pagesize ();
  96. return size & ~(page_size - 1);
  97. }
  98. static void
  99. file_mmap_init (void)
  100. {
  101. retry:
  102. switch (mmap_init_state) {
  103. case 0:
  104. if (mono_atomic_cas_i32 (&mmap_init_state, 1, 0) != 0)
  105. goto retry;
  106. named_regions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
  107. mono_coop_mutex_init (&named_regions_mutex);
  108. mono_atomic_store_release (&mmap_init_state, 2);
  109. break;
  110. case 1:
  111. do {
  112. mono_thread_info_sleep (1, NULL); /* Been init'd by other threads, this is very rare. */
  113. } while (mmap_init_state != 2);
  114. break;
  115. case 2:
  116. break;
  117. default:
  118. g_error ("Invalid init state %d", mmap_init_state);
  119. }
  120. }
  121. static void
  122. named_regions_lock (void)
  123. {
  124. file_mmap_init ();
  125. mono_coop_mutex_lock (&named_regions_mutex);
  126. }
  127. static void
  128. named_regions_unlock (void)
  129. {
  130. mono_coop_mutex_unlock (&named_regions_mutex);
  131. }
  132. static int
  133. file_mode_to_unix (int mode)
  134. {
  135. switch (mode) {
  136. case FILE_MODE_CREATE_NEW:
  137. return O_CREAT | O_EXCL;
  138. case FILE_MODE_CREATE:
  139. return O_CREAT | O_TRUNC;
  140. case FILE_MODE_OPEN:
  141. return 0;
  142. case FILE_MODE_OPEN_OR_CREATE:
  143. return O_CREAT;
  144. case FILE_MODE_TRUNCATE:
  145. return O_TRUNC;
  146. case FILE_MODE_APPEND:
  147. return O_APPEND;
  148. default:
  149. g_error ("unknown FileMode %d", mode);
  150. }
  151. }
  152. static int
  153. access_mode_to_unix (int access)
  154. {
  155. switch (access) {
  156. case MMAP_FILE_ACCESS_READ_WRITE:
  157. case MMAP_FILE_ACCESS_COPY_ON_WRITE:
  158. case MMAP_FILE_ACCESS_READ_WRITE_EXECUTE:
  159. return O_RDWR;
  160. case MMAP_FILE_ACCESS_READ:
  161. case MMAP_FILE_ACCESS_READ_EXECUTE:
  162. return O_RDONLY;
  163. case MMAP_FILE_ACCESS_WRITE:
  164. return O_WRONLY;
  165. default:
  166. g_error ("unknown MemoryMappedFileAccess %d", access);
  167. }
  168. }
  169. static int
  170. access_to_mmap_flags (int access)
  171. {
  172. switch (access) {
  173. case MMAP_FILE_ACCESS_READ_WRITE:
  174. return MONO_MMAP_WRITE | MONO_MMAP_READ | MONO_MMAP_SHARED;
  175. case MMAP_FILE_ACCESS_WRITE:
  176. return MONO_MMAP_WRITE | MONO_MMAP_SHARED;
  177. case MMAP_FILE_ACCESS_COPY_ON_WRITE:
  178. return MONO_MMAP_WRITE | MONO_MMAP_READ | MONO_MMAP_PRIVATE;
  179. case MMAP_FILE_ACCESS_READ_EXECUTE:
  180. return MONO_MMAP_EXEC | MONO_MMAP_PRIVATE | MONO_MMAP_SHARED;
  181. case MMAP_FILE_ACCESS_READ_WRITE_EXECUTE:
  182. return MONO_MMAP_WRITE | MONO_MMAP_READ | MONO_MMAP_EXEC | MONO_MMAP_SHARED;
  183. case MMAP_FILE_ACCESS_READ:
  184. return MONO_MMAP_READ | MONO_MMAP_SHARED;
  185. default:
  186. g_error ("unknown MemoryMappedFileAccess %d", access);
  187. }
  188. }
  189. /*
  190. This allow us to special case zero size files that can be arbitrarily mapped.
  191. */
  192. static gboolean
  193. is_special_zero_size_file (struct stat *buf)
  194. {
  195. return buf->st_size == 0 && (buf->st_mode & (S_IFCHR | S_IFBLK | S_IFIFO | S_IFSOCK)) != 0;
  196. }
  197. /*
  198. XXX implement options
  199. */
  200. static void*
  201. open_file_map (const char *c_path, int input_fd, int mode, gint64 *capacity, int access, int options, int *ioerror)
  202. {
  203. struct stat buf;
  204. MmapHandle *handle = NULL;
  205. int result, fd;
  206. MONO_ENTER_GC_SAFE;
  207. if (c_path)
  208. result = stat (c_path, &buf);
  209. else
  210. result = fstat (input_fd, &buf);
  211. MONO_EXIT_GC_SAFE;
  212. if (mode == FILE_MODE_TRUNCATE || mode == FILE_MODE_APPEND || mode == FILE_MODE_OPEN) {
  213. if (result == -1) { //XXX translate errno?
  214. *ioerror = FILE_NOT_FOUND;
  215. goto done;
  216. }
  217. }
  218. if (mode == FILE_MODE_CREATE_NEW && result == 0) {
  219. *ioerror = FILE_ALREADY_EXISTS;
  220. goto done;
  221. }
  222. if (result == 0) {
  223. if (*capacity == 0) {
  224. /**
  225. * Special files such as FIFOs, sockets, and devices can have a size of 0. Specifying a capacity for these
  226. * also makes little sense, so don't do the check if th file is one of these.
  227. */
  228. if (buf.st_size == 0 && !is_special_zero_size_file (&buf)) {
  229. *ioerror = CAPACITY_SMALLER_THAN_FILE_SIZE;
  230. goto done;
  231. }
  232. *capacity = buf.st_size;
  233. } else if (*capacity < buf.st_size) {
  234. *ioerror = CAPACITY_SMALLER_THAN_FILE_SIZE;
  235. goto done;
  236. }
  237. } else {
  238. if (mode == FILE_MODE_CREATE_NEW && *capacity == 0) {
  239. *ioerror = CAPACITY_SMALLER_THAN_FILE_SIZE;
  240. goto done;
  241. }
  242. }
  243. MONO_ENTER_GC_SAFE;
  244. if (c_path) //FIXME use io portability?
  245. fd = open (c_path, file_mode_to_unix (mode) | access_mode_to_unix (access), DEFAULT_FILEMODE);
  246. else
  247. fd = dup (input_fd);
  248. MONO_EXIT_GC_SAFE;
  249. if (fd == -1) { //XXX translate errno?
  250. *ioerror = COULD_NOT_OPEN;
  251. goto done;
  252. }
  253. if (result != 0 || *capacity > buf.st_size) {
  254. #ifdef HAVE_FTRUNCATE
  255. int unused G_GNUC_UNUSED = ftruncate (fd, (off_t)*capacity);
  256. #endif
  257. }
  258. handle = g_new0 (MmapHandle, 1);
  259. handle->ref_count = 1;
  260. handle->capacity = *capacity;
  261. handle->fd = fd;
  262. done:
  263. return (void*)handle;
  264. }
  265. #define MONO_ANON_FILE_TEMPLATE "/mono.anonmap.XXXXXXXXX"
  266. static void*
  267. open_memory_map (const char *c_mapName, int mode, gint64 *capacity, int access, int options, int *ioerror)
  268. {
  269. MmapHandle *handle;
  270. if (*capacity <= 0 && mode != FILE_MODE_OPEN) {
  271. *ioerror = CAPACITY_MUST_BE_POSITIVE;
  272. return NULL;
  273. }
  274. #if SIZEOF_VOID_P == 4
  275. if (*capacity > UINT32_MAX) {
  276. *ioerror = CAPACITY_LARGER_THAN_LOGICAL_ADDRESS_SPACE;
  277. return NULL;
  278. }
  279. #endif
  280. if (!(mode == FILE_MODE_CREATE_NEW || mode == FILE_MODE_OPEN_OR_CREATE || mode == FILE_MODE_OPEN)) {
  281. *ioerror = INVALID_FILE_MODE;
  282. return NULL;
  283. }
  284. named_regions_lock ();
  285. handle = (MmapHandle*)g_hash_table_lookup (named_regions, c_mapName);
  286. if (handle) {
  287. if (mode == FILE_MODE_CREATE_NEW) {
  288. *ioerror = FILE_ALREADY_EXISTS;
  289. goto done;
  290. }
  291. handle->ref_count++;
  292. //XXX should we ftruncate if the file is smaller than capacity?
  293. } else {
  294. int fd;
  295. char *file_name;
  296. const char *tmp_dir;
  297. int unused G_GNUC_UNUSED, alloc_size;
  298. if (mode == FILE_MODE_OPEN) {
  299. *ioerror = FILE_NOT_FOUND;
  300. goto done;
  301. }
  302. *capacity = align_up_to_page_size (*capacity);
  303. tmp_dir = g_get_tmp_dir ();
  304. alloc_size = strlen (tmp_dir) + strlen (MONO_ANON_FILE_TEMPLATE) + 1;
  305. if (alloc_size > 1024) {//rather fail that stack overflow
  306. *ioerror = COULD_NOT_MAP_MEMORY;
  307. goto done;
  308. }
  309. file_name = g_newa (char, alloc_size);
  310. strcpy (file_name, tmp_dir);
  311. strcat (file_name, MONO_ANON_FILE_TEMPLATE);
  312. MONO_ENTER_GC_SAFE;
  313. fd = mkstemp (file_name);
  314. MONO_EXIT_GC_SAFE;
  315. if (fd == -1) {
  316. *ioerror = COULD_NOT_MAP_MEMORY;
  317. goto done;
  318. }
  319. MONO_ENTER_GC_SAFE;
  320. unlink (file_name);
  321. MONO_EXIT_GC_SAFE;
  322. #ifdef HAVE_FTRUNCATE
  323. unused = ftruncate (fd, (off_t)*capacity);
  324. #endif
  325. handle = g_new0 (MmapHandle, 1);
  326. handle->ref_count = 1;
  327. handle->capacity = *capacity;
  328. handle->fd = fd;
  329. handle->name = g_strdup (c_mapName);
  330. g_hash_table_insert (named_regions, handle->name, handle);
  331. }
  332. done:
  333. named_regions_unlock ();
  334. return handle;
  335. }
  336. /* This is an icall */
  337. void *
  338. mono_mmap_open_file (const gunichar2 *path, gint path_length, int mode, const gunichar2 *mapName, gint mapName_length, gint64 *capacity, int access, int options, int *ioerror, MonoError *error)
  339. {
  340. MmapHandle *handle = NULL;
  341. g_assert (path || mapName);
  342. if (!mapName) {
  343. char * c_path = mono_utf16_to_utf8 (path, path_length, error);
  344. return_val_if_nok (error, NULL);
  345. handle = (MmapHandle*)open_file_map (c_path, -1, mode, capacity, access, options, ioerror);
  346. g_free (c_path);
  347. return handle;
  348. }
  349. char *c_mapName = mono_utf16_to_utf8 (mapName, mapName_length, error);
  350. return_val_if_nok (error, NULL);
  351. if (path) {
  352. named_regions_lock ();
  353. handle = (MmapHandle*)g_hash_table_lookup (named_regions, c_mapName);
  354. if (handle) {
  355. *ioerror = FILE_ALREADY_EXISTS;
  356. handle = NULL;
  357. } else {
  358. char *c_path = mono_utf16_to_utf8 (path, path_length, error);
  359. if (is_ok (error)) {
  360. handle = (MmapHandle *)open_file_map (c_path, -1, mode, capacity, access, options, ioerror);
  361. if (handle) {
  362. handle->name = g_strdup (c_mapName);
  363. g_hash_table_insert (named_regions, handle->name, handle);
  364. }
  365. } else {
  366. handle = NULL;
  367. }
  368. g_free (c_path);
  369. }
  370. named_regions_unlock ();
  371. } else
  372. handle = (MmapHandle*)open_memory_map (c_mapName, mode, capacity, access, options, ioerror);
  373. g_free (c_mapName);
  374. return handle;
  375. }
  376. /* this is an icall */
  377. void *
  378. mono_mmap_open_handle (void *input_fd, const mono_unichar2 *mapName, gint mapName_length, gint64 *capacity, int access, int options, int *ioerror, MonoError *error)
  379. {
  380. MmapHandle *handle;
  381. if (!mapName) {
  382. handle = (MmapHandle *)open_file_map (NULL, GPOINTER_TO_INT (input_fd), FILE_MODE_OPEN, capacity, access, options, ioerror);
  383. } else {
  384. char *c_mapName = mono_utf16_to_utf8 (mapName, mapName_length, error);
  385. return_val_if_nok (error, NULL);
  386. named_regions_lock ();
  387. handle = (MmapHandle*)g_hash_table_lookup (named_regions, c_mapName);
  388. if (handle) {
  389. *ioerror = FILE_ALREADY_EXISTS;
  390. handle = NULL;
  391. } else {
  392. //XXX we're exploiting wapi HANDLE == FD equivalence. THIS IS FRAGILE, create a _wapi_handle_to_fd call
  393. handle = (MmapHandle *)open_file_map (NULL, GPOINTER_TO_INT (input_fd), FILE_MODE_OPEN, capacity, access, options, ioerror);
  394. handle->name = g_strdup (c_mapName);
  395. g_hash_table_insert (named_regions, handle->name, handle);
  396. }
  397. named_regions_unlock ();
  398. g_free (c_mapName);
  399. }
  400. return handle;
  401. }
  402. void
  403. mono_mmap_close (void *mmap_handle, MonoError *error)
  404. {
  405. MmapHandle *handle = (MmapHandle *)mmap_handle;
  406. named_regions_lock ();
  407. --handle->ref_count;
  408. if (handle->ref_count == 0) {
  409. if (handle->name)
  410. g_hash_table_remove (named_regions, handle->name);
  411. g_free (handle->name);
  412. MONO_ENTER_GC_SAFE;
  413. close (handle->fd);
  414. MONO_EXIT_GC_SAFE;
  415. g_free (handle);
  416. }
  417. named_regions_unlock ();
  418. }
  419. void
  420. mono_mmap_configure_inheritability (void *mmap_handle, gint32 inheritability, MonoError *error)
  421. {
  422. MmapHandle *h = (MmapHandle *)mmap_handle;
  423. int fd, flags;
  424. fd = h->fd;
  425. MONO_ENTER_GC_SAFE;
  426. flags = fcntl (fd, F_GETFD, 0);
  427. MONO_EXIT_GC_SAFE;
  428. if (inheritability)
  429. flags &= ~FD_CLOEXEC;
  430. else
  431. flags |= FD_CLOEXEC;
  432. fcntl (fd, F_SETFD, flags);
  433. }
  434. void
  435. mono_mmap_flush (void *mmap_handle, MonoError *error)
  436. {
  437. MmapInstance *h = (MmapInstance *)mmap_handle;
  438. #ifdef HAVE_MSYNC
  439. if (h)
  440. MONO_ENTER_GC_SAFE;
  441. msync (h->address, h->length, MS_SYNC);
  442. MONO_EXIT_GC_SAFE;
  443. #endif
  444. }
  445. int
  446. mono_mmap_map (void *handle, gint64 offset, gint64 *size, int access, void **mmap_handle, void **base_address, MonoError *error)
  447. {
  448. gint64 mmap_offset = 0;
  449. MmapHandle *fh = (MmapHandle *)handle;
  450. MmapInstance res = { 0 };
  451. size_t eff_size = *size;
  452. struct stat buf = { 0 };
  453. fstat (fh->fd, &buf); //FIXME error handling
  454. *mmap_handle = NULL;
  455. *base_address = NULL;
  456. if (offset > buf.st_size || ((eff_size + offset) > buf.st_size && !is_special_zero_size_file (&buf)))
  457. return ACCESS_DENIED;
  458. /**
  459. * We use the file size if one of the following conditions is true:
  460. * -input size is zero
  461. * -input size is bigger than the file and the file is not a magical zero size file such as /dev/mem.
  462. */
  463. if (eff_size == 0)
  464. eff_size = align_up_to_page_size (buf.st_size) - offset;
  465. *size = eff_size;
  466. mmap_offset = align_down_to_page_size (offset);
  467. eff_size += (offset - mmap_offset);
  468. MONO_ENTER_GC_SAFE;
  469. //FIXME translate some interesting errno values
  470. res.address = mono_file_map ((size_t)eff_size, access_to_mmap_flags (access), fh->fd, mmap_offset, &res.free_handle);
  471. MONO_EXIT_GC_SAFE;
  472. res.length = eff_size;
  473. if (res.address) {
  474. *mmap_handle = g_memdup (&res, sizeof (MmapInstance));
  475. *base_address = (char*)res.address + (offset - mmap_offset);
  476. return 0;
  477. }
  478. return COULD_NOT_MAP_MEMORY;
  479. }
  480. MonoBoolean
  481. mono_mmap_unmap (void *base_address, MonoError *error)
  482. {
  483. int res = 0;
  484. MmapInstance *h = (MmapInstance *)base_address;
  485. MONO_ENTER_GC_SAFE;
  486. res = mono_file_unmap (h->address, h->free_handle);
  487. MONO_EXIT_GC_SAFE;
  488. g_free (h);
  489. return res == 0;
  490. }
  491. #endif