File Coverage

lib/File/Map.xs
Criterion Covered Total %
statement 204 248 82.2
branch 126 250 50.4
condition n/a
subroutine n/a
pod n/a
total 330 498 66.2


line stmt bran cond sub pod time code
1             /*
2             * This software is copyright (c) 2008, 2009 by Leon Timmermans .
3             *
4             * This is free software; you can redistribute it and/or modify it under
5             * the same terms as perl itself.
6             *
7             */
8              
9             #if defined(linux) && !defined(_GNU_SOURCE)
10             # define _GNU_SOURCE
11             #endif
12              
13             #ifdef __CYGWIN__
14             # undef WIN32
15             # undef _WIN32
16             # define madvise posix_madvise
17             #endif
18              
19             #include
20             #ifdef WIN32
21             # include
22             # include
23             # define PROT_NONE 0
24             # define PROT_READ 1
25             # define PROT_WRITE 2
26             # define PROT_EXEC 4
27             # define MAP_SHARED 0
28             # define MAP_PRIVATE 1
29             # define MAP_ANONYMOUS 2
30             #else /* WIN32 */
31             # include
32             # include
33             # include
34             # include
35             #endif /* WIN32 */
36              
37             #ifdef __QNX__
38             # define madvise posix_madvise
39             #endif
40              
41             #ifndef MAP_ANONYMOUS
42             # define MAP_ANONYMOUS MAP_ANON
43             #elif !defined MAP_ANON
44             # define MAP_ANON MAP_ANONYMOUS
45             #endif /* MAP_ANONYMOUS */
46              
47             #ifndef MAP_FILE
48             # define MAP_FILE 0
49             #endif
50              
51             #ifndef MAP_VARIABLE
52             # define MAP_VARIABLE 0
53             #endif
54              
55             #ifndef MIN
56             # define MIN(a, b) ((a) < (b) ? (a) : (b))
57             #endif
58              
59             #define PERL_NO_GET_CONTEXT
60             #define PERL_REENTR_API 1
61             #include "EXTERN.h"
62             #include "perl.h"
63             #include "XSUB.h"
64             #include "perliol.h"
65             #define NEED_mg_findext
66             #define NEED_sv_unmagicext
67             #include "ppport.h"
68              
69             #ifndef SvPV_free
70             # define SvPV_free(arg) sv_setpvn_mg(arg, NULL, 0);
71             #endif
72              
73             #ifndef SV_CHECK_THINKFIRST_COW_DROP
74             #define SV_CHECK_THINKFIRST_COW_DROP(sv) SV_CHECK_THINKFIRST(sv)
75             #endif
76              
77             struct mmap_info {
78             void* real_address;
79             void* fake_address;
80             size_t real_length;
81             size_t fake_length;
82             int flags;
83             #ifdef USE_ITHREADS
84             perl_mutex count_mutex;
85             perl_mutex data_mutex;
86             PerlInterpreter* owner;
87             perl_cond cond;
88             int count;
89             #endif
90             };
91              
92             #ifdef WIN32
93              
94             static DWORD page_size() {
95             static DWORD pagesize = 0;
96             if (pagesize == 0) {
97             SYSTEM_INFO info;
98             GetSystemInfo(&info);
99             pagesize = info.dwPageSize;
100             }
101             return pagesize;
102             }
103              
104             static DWORD old_protect;
105              
106             #define munmap(address, length) ( UnmapViewOfFile(address) ? 0 : -1 )
107             #define msync(address, length, flags) ( FlushViewOfFile(address, length) ? 0 : -1 )
108             #define mlock(address, length) ( VirtualLock(address, length) ? 0 : -1 )
109             #define munlock(address, length) ( VirtualUnlock(address, length) ? 0 : -1 )
110             #define mprotect(address, length, prot) ( VirtualProtect(address, length, winflags[prot & PROT_ALL].createflag, &old_protect) ? 0 : -1 )
111              
112             #ifndef FILE_MAP_EXECUTE
113             # define FILE_MAP_EXECUTE 0
114             #endif
115              
116             static const struct {
117             DWORD createflag;
118             DWORD viewflag;
119             } winflags[] = {
120             { 0 , 0 }, /* PROT_NONE */
121             { PAGE_READONLY , FILE_MAP_READ }, /* PROT_READ */
122             { PAGE_READWRITE, FILE_MAP_WRITE }, /* PROT_WRITE */
123             { PAGE_READWRITE, FILE_MAP_ALL_ACCESS }, /* PROT_READ | PROT_WRITE */
124             { PAGE_EXECUTE_READ , FILE_MAP_READ | FILE_MAP_EXECUTE}, /* PROT_NONE | PROT_EXEC */
125             { PAGE_EXECUTE_READ , FILE_MAP_READ | FILE_MAP_EXECUTE}, /* PROT_READ | PROT_EXEC */
126             { PAGE_EXECUTE_READWRITE, FILE_MAP_WRITE | FILE_MAP_EXECUTE}, /* PROT_WRITE | PROT_EXEC */
127             { PAGE_EXECUTE_READWRITE, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE}, /* PROT_READ| PROT_WRITE | PROT_EXEC */
128             };
129              
130             #define madvise(address, length, advice) 0
131              
132             #else
133              
134             static size_t page_size() {
135             static size_t pagesize = 0;
136 20 100         if (pagesize == 0) {
137 7           pagesize = sysconf(_SC_PAGESIZE);
138             }
139 20           return pagesize;
140             }
141             #endif
142              
143             #ifdef VMS
144             #define madvise(address, length, advice) 0
145             #endif
146              
147             #ifndef MADV_NORMAL
148             # define MADV_NORMAL 0
149             # define MADV_RANDOM 0
150             # define MADV_SEQUENTIAL 0
151             # define MADV_WILLNEED 0
152             # define MADV_DONTNEED 0
153             #endif
154              
155             #define die_sys(format) Perl_croak(aTHX_ format, strerror(errno))
156              
157             #define PROT_ALL (PROT_READ | PROT_WRITE | PROT_EXEC)
158              
159             static void reset_var(SV* var, struct mmap_info* info) {
160 30           SvPVX(var) = info->fake_address;
161 30           SvLEN(var) = 0;
162 30           SvCUR(var) = info->fake_length;
163 30           SvPOK_only_UTF8(var);
164             }
165              
166 9           static void mmap_fixup(pTHX_ SV* var, struct mmap_info* info, const char* string, STRLEN len) {
167 9 100         if (ckWARN(WARN_SUBSTR)) {
168 6           Perl_warn(aTHX_ "Writing directly to a memory mapped file is not recommended");
169 6 100         if (SvCUR(var) > info->fake_length)
170 1           Perl_warn(aTHX_ "Truncating new value to size of the memory map");
171             }
172              
173 9 100         if (string && len)
174 8           Copy(string, info->fake_address, MIN(len, info->fake_length), char);
175 9 50         SV_CHECK_THINKFIRST_COW_DROP(var);
176 9 50         if (SvROK(var))
177 0           sv_unref_flags(var, SV_IMMEDIATE_UNREF);
178 9 100         if (SvPOK(var))
179 5 50         SvPV_free(var);
    50          
    0          
    0          
180             reset_var(var, info);
181 9           }
182              
183 26           static int mmap_write(pTHX_ SV* var, MAGIC* magic) {
184 26           struct mmap_info* info = (struct mmap_info*) magic->mg_ptr;
185 26 100         if (info->real_length) {
186 25 100         if (!SvOK(var))
    50          
    50          
187 1           mmap_fixup(aTHX_ var, info, NULL, 0);
188 24 100         else if (!SvPOK(var)) {
189             STRLEN len;
190 3 50         const char* string = SvPV(var, len);
191 3           mmap_fixup(aTHX_ var, info, string, len);
192             }
193 21 100         else if (SvPVX(var) != info->fake_address)
194 5           mmap_fixup(aTHX_ var, info, SvPVX(var), SvCUR(var));
195             else {
196 16 50         if (ckWARN(WARN_SUBSTR) && SvCUR(var) != info->fake_length) {
    100          
197 1           Perl_warn(aTHX_ "Writing directly to a memory mapped file is not recommended");
198 1           SvCUR(var) = info->fake_length;
199             }
200 16           SvPOK_only_UTF8(var);
201             }
202             }
203             else {
204 1 50         if (!SvPOK(var) || sv_len(var) != 0) {
    50          
205 1           sv_setpvn(var, "", 0);
206 1 50         if (ckWARN(WARN_SUBSTR))
207 1           Perl_warn(aTHX_ "Can't overwrite an empty map");
208             }
209 1           SvPOK_only_UTF8(var);
210             }
211 26           return 0;
212             }
213              
214 0           static int mmap_clear(pTHX_ SV* var, MAGIC* magic) {
215 0           Perl_die(aTHX_ "Can't clear a mapped variable");
216             return 0;
217             }
218              
219 18           static int mmap_free(pTHX_ SV* var, MAGIC* magic) {
220 18           struct mmap_info* info = (struct mmap_info*) magic->mg_ptr;
221             #ifdef USE_ITHREADS
222             MUTEX_LOCK(&info->count_mutex);
223             if (--info->count == 0) {
224             if (info->real_length && munmap(info->real_address, info->real_length) == -1)
225             die_sys("Could not unmap: %s");
226             COND_DESTROY(&info->cond);
227             MUTEX_DESTROY(&info->data_mutex);
228             MUTEX_UNLOCK(&info->count_mutex);
229             MUTEX_DESTROY(&info->count_mutex);
230             PerlMemShared_free(info);
231             }
232             else {
233             if (info->real_length && msync(info->real_address, info->real_length, MS_ASYNC) == -1)
234             die_sys("Could not sync: %s");
235             MUTEX_UNLOCK(&info->count_mutex);
236             }
237             #else
238 18 100         if (info->real_length && munmap(info->real_address, info->real_length) == -1)
    50          
239 0           die_sys("Could not unmap: %s");
240 18           PerlMemShared_free(info);
241             #endif
242 18           SvREADONLY_off(var);
243 18           SvPVX(var) = NULL;
244 18           SvCUR(var) = 0;
245 18           return 0;
246             }
247              
248             #ifdef USE_ITHREADS
249             static int mmap_dup(pTHX_ MAGIC* magic, CLONE_PARAMS* param) {
250             struct mmap_info* info = (struct mmap_info*) magic->mg_ptr;
251             MUTEX_LOCK(&info->count_mutex);
252             assert(info->count);
253             ++info->count;
254             MUTEX_UNLOCK(&info->count_mutex);
255             return 0;
256             }
257             #else
258             #define mmap_dup 0
259             #endif
260              
261             #ifdef MGf_LOCAL
262 1           static int mmap_local(pTHX_ SV* var, MAGIC* magic) {
263 1           Perl_croak(aTHX_ "Can't localize file map");
264             }
265             #define mmap_local_tail , mmap_local
266             #else
267             #define mmap_local_tail
268             #endif
269              
270             static const MGVTBL mmap_table = { 0, mmap_write, 0, mmap_clear, mmap_free, 0, mmap_dup mmap_local_tail };
271              
272 1           static Off_t S_sv_to_offset(pTHX_ SV* var) {
273             #if IV_SIZE >= 8
274             return (Off_t)SvUV(var);
275             #else
276 1 50         return (Off_t)floor(SvNV(var) + 0.5); /* hic sunt dracones */
277             #endif
278             }
279             #define sv_to_offset(var) S_sv_to_offset(aTHX_ var)
280              
281 21           static void check_new_variable(pTHX_ SV* var) {
282 21 50         if (SvTYPE(var) > SVt_PVMG && SvTYPE(var) != SVt_PVLV)
283 0           Perl_croak(aTHX_ "Trying to map into a nonscalar!\n");
284 21 100         SV_CHECK_THINKFIRST_COW_DROP(var);
285 20 50         if (SvREADONLY(var))
286 0           Perl_croak(aTHX_ "%s", PL_no_modify);
287 20 100         if (SvMAGICAL(var) && mg_findext(var, PERL_MAGIC_ext, &mmap_table))
    50          
288 0           sv_unmagicext(var, PERL_MAGIC_ext, (MGVTBL*)&mmap_table);
289 20 50         if (SvROK(var))
290 0           sv_unref_flags(var, SV_IMMEDIATE_UNREF);
291 20 50         if (SvNIOK(var))
292 0           SvNIOK_off(var);
293 20 50         if (SvPOK(var))
294 0 0         SvPV_free(var);
    0          
    0          
    0          
295 20 100         SvUPGRADE(var, SVt_PVMG);
296 20           }
297              
298             #define BITS32_MASK 0xFFFFFFFF
299              
300 17           static void* do_mapping(pTHX_ size_t length, int prot, int flags, int fd, Off_t offset) {
301             void* address;
302             #ifdef WIN32
303             HANDLE file;
304             HANDLE mapping;
305             DWORD viewflag;
306             Off_t maxsize = offset + length;
307             prot &= PROT_ALL;
308             file = (flags & MAP_ANONYMOUS) ? INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle(fd);
309             mapping = CreateFileMapping(file, NULL, winflags[prot].createflag, maxsize >> 32, maxsize & BITS32_MASK, NULL);
310             if (mapping == NULL)
311             die_sys("Could not map: %s");
312             viewflag = (flags & MAP_PRIVATE) ? (FILE_MAP_COPY | ( prot & PROT_EXEC ? FILE_MAP_EXECUTE : 0 ) ) : winflags[prot].viewflag;
313             address = MapViewOfFile(mapping, viewflag, offset >> 32, offset & BITS32_MASK, length);
314             CloseHandle(mapping);
315             if (address == NULL)
316             #else
317 17           address = mmap(0, length, prot, flags | MAP_VARIABLE, fd, offset);
318 17 50         if (address == MAP_FAILED)
319             #endif
320 0           die_sys("Could not map: %s");
321 17           return address;
322             }
323              
324             static void S_set_mmap_info(pTHX_ struct mmap_info* magical, void* address, size_t length, ptrdiff_t correction) {
325 21           magical->real_address = address;
326 21           magical->fake_address = (char*)address + correction;
327 21           magical->real_length = length + correction;
328 21           magical->fake_length = length;
329             #ifdef USE_ITHREADS
330             MUTEX_INIT(&magical->count_mutex);
331             MUTEX_INIT(&magical->data_mutex);
332             COND_INIT(&magical->cond);
333             magical->count = 1;
334             #endif
335             }
336             #define set_mmap_info(magical, addres, length, correction) S_set_mmap_info(aTHX_ magical, addres, length, correction)
337              
338             static struct mmap_info* initialize_mmap_info(pTHX_ void* address, size_t length, ptrdiff_t correction, int flags) {
339 19           struct mmap_info* magical = PerlMemShared_malloc(sizeof *magical);
340             set_mmap_info(magical, address, length, correction);
341 19           magical->flags = flags;
342             return magical;
343             }
344              
345 19           static void add_magic(pTHX_ SV* var, struct mmap_info* magical, int writable, int utf8) {
346 19           MAGIC* magic = sv_magicext(var, NULL, PERL_MAGIC_ext, &mmap_table, (const char*) magical, 0);
347             #ifdef MGf_LOCAL
348 19           magic->mg_flags |= MGf_LOCAL;
349             #endif
350             #ifdef USE_ITHREADS
351             magic->mg_flags |= MGf_DUP;
352             #endif
353 19 100         SvTAINTED_on(var);
354 19 100         if (utf8 && !sv_utf8_decode(var))
    50          
355 0           Perl_croak(aTHX_ "Invalid utf8 in memory mapping");
356 19 100         if (!writable)
357 9           SvREADONLY_on(var);
358 19           }
359              
360 3           static int _is_mappable(pTHX_ int fd) {
361             Stat_t info;
362 3 50         return Fstat(fd, &info) == 0 && (S_ISREG(info.st_mode) || S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
    100          
    50          
363             }
364              
365             #define is_mappable(fd) _is_mappable(aTHX_ fd)
366              
367 12           static struct mmap_info* get_mmap_magic(pTHX_ SV* var, const char* funcname) {
368             MAGIC* magic;
369 12 100         if (!SvMAGICAL(var) || (magic = mg_findext(var, PERL_MAGIC_ext, &mmap_table)) == NULL)
    50          
370 3           Perl_croak(aTHX_ "Could not %s: this variable is not memory mapped", funcname);
371 9           return (struct mmap_info*) magic->mg_ptr;
372             }
373              
374             #ifdef USE_ITHREADS
375             static void magic_end(pTHX_ void* pre_info) {
376             struct mmap_info* info = (struct mmap_info*) pre_info;
377             info->owner = NULL;
378             MUTEX_UNLOCK(&info->data_mutex);
379             }
380             #endif
381              
382             typedef struct { const char* key; size_t length; int value; } map[];
383              
384             static map prots = {
385             { STR_WITH_LEN("<"), PROT_READ },
386             { STR_WITH_LEN("+<"), PROT_READ | PROT_WRITE },
387             { STR_WITH_LEN(">"), PROT_WRITE },
388             { STR_WITH_LEN("+>"), PROT_READ | PROT_WRITE },
389             };
390              
391 16           static int S_protection_pvn(pTHX_ const char* mode, size_t mode_len) {
392             int i;
393 24 50         for (i = 0; i < sizeof prots / sizeof *prots; ++i) {
394 24 100         if (prots[i].length == mode_len && strnEQ(mode, prots[i].key, mode_len))
    100          
395 16           return prots[i].value;
396             }
397 0           Perl_croak(aTHX_ "No such mode '%s' known", mode);
398             }
399             #define protection_pvn(mode, mode_len) S_protection_pvn(aTHX_ mode, mode_len)
400              
401 16           static int S_protection_sv(pTHX_ SV* mode_sv) {
402             STRLEN mode_len;
403 16 50         const char* mode = SvPV(mode_sv, mode_len);
404 16           const char* end = memchr(mode, ':', mode_len);
405 16 100         return protection_pvn(mode, end ? end - mode : mode_len);
406             }
407             #define protection_sv(mode) S_protection_sv(aTHX_ mode)
408              
409             #define YES &PL_sv_yes
410              
411             #define MAP_CONSTANT(cons) newCONSTSUB(stash, #cons, newSVuv(cons))
412             #define ADVISE_CONSTANT(key, value) hv_store(advise_constants, key, sizeof key - 1, newSVuv(value), 0)
413              
414             #define EMPTY_MAP(info) ((info)->real_length == 0)
415              
416 7           static void boot(pTHX) {
417 7           HV* stash = get_hv("File::Map::", FALSE);
418 7           HV* advise_constants = newHV();
419              
420 7           MAP_CONSTANT(PROT_NONE);
421 7           MAP_CONSTANT(PROT_READ);
422 7           MAP_CONSTANT(PROT_WRITE);
423 7           MAP_CONSTANT(PROT_EXEC);
424 7           MAP_CONSTANT(MAP_ANONYMOUS);
425 7           MAP_CONSTANT(MAP_SHARED);
426 7           MAP_CONSTANT(MAP_PRIVATE);
427 7           MAP_CONSTANT(MAP_ANON);
428 7           MAP_CONSTANT(MAP_FILE);
429             /**/
430            
431 7           hv_store(PL_modglobal, "File::Map::ADVISE_CONSTANTS", 27, (SV*)advise_constants, 0);
432 7           ADVISE_CONSTANT("normal", MADV_NORMAL);
433 7           ADVISE_CONSTANT("random", MADV_RANDOM);
434 7           ADVISE_CONSTANT("sequential", MADV_SEQUENTIAL);
435 7           ADVISE_CONSTANT("willneed", MADV_WILLNEED);
436 7           ADVISE_CONSTANT("dontneed", MADV_DONTNEED);
437             /* Linux specific advices */
438             #ifdef MADV_REMOVE
439 7           ADVISE_CONSTANT("remove", MADV_REMOVE);
440             #endif
441             #ifdef MADV_DONTFORK
442 7           ADVISE_CONSTANT("dontfork", MADV_DONTFORK);
443             #endif
444             #ifdef MADV_DOFORK
445 7           ADVISE_CONSTANT("dofork", MADV_DOFORK);
446             #endif
447             #ifdef MADV_MERGEABLE
448 7           ADVISE_CONSTANT("mergeable", MADV_MERGEABLE);
449             #endif
450             #ifdef MADV_UNMERGEABLE
451 7           ADVISE_CONSTANT("unmergeable", MADV_UNMERGEABLE);
452             #endif
453             /* BSD, Mac OS X & Solaris specific advice */
454             #ifdef MADV_FREE
455             ADVISE_CONSTANT("free", MADV_FREE);
456             #endif
457             /* FreeBSD specific advices */
458             #ifdef MADV_NOSYNC
459             ADVISE_CONSTANT("nosync", MADV_NOSYNC);
460             #endif
461             #ifdef MADV_AUTOSYNC
462             ADVISE_CONSTANT("autosync", MADV_AUTOSYNC);
463             #endif
464             #ifdef MADV_NOCORE
465             ADVISE_CONSTANT("nocore", MADV_NOCORE);
466             #endif
467             #ifdef MADV_CORE
468             ADVISE_CONSTANT("core", MADV_CORE);
469             #endif
470             #ifdef MADV_PROTECT
471             ADVISE_CONSTANT("protect", MADV_PROTECT);
472             #endif
473             #ifdef MADV_SPACEAVAIL
474             ADVISE_CONSTANT("spaceavail", MADV_SPACEAVAIL);
475             #endif
476 7           }
477              
478             #if PTRSIZE == 8 && (defined(WIN32) || defined(__CYGWIN__))
479             #ifndef ULLONG_MAX
480             #define PTR_MAX _UI64_MAX /* MS Platform SDK crt */
481             #else
482             #define PTR_MAX ULLONG_MAX
483             #endif
484             #else
485             #define PTR_MAX ULONG_MAX
486             #endif
487              
488 21           void S_mmap_impl(pTHX_ SV* var, size_t length, int prot, int flags, int fd, Off_t offset, int utf8) {
489 21           check_new_variable(aTHX_ var);
490              
491 40           ptrdiff_t correction = offset % page_size();
492             void* address;
493             struct mmap_info* magical;
494 20 50         if (length > PTR_MAX - correction)
495 0           Perl_croak(aTHX_ "can't map: length + offset overflows");
496              
497 20 100         if (length)
498 17           address = do_mapping(aTHX_ length + correction, prot, flags, fd, offset - correction);
499             else {
500 3 100         if (!is_mappable(fd)) {
501 1           errno = EACCES;
502 1           die_sys("Could not map: %s");
503             }
504             address = "";
505             correction = 0;
506             }
507              
508             magical = initialize_mmap_info(aTHX_ address, length, correction, flags);
509             reset_var(var, magical);
510 19 100         SvSETMAGIC(var);
511 19           add_magic(aTHX_ var, magical, prot & PROT_WRITE, utf8);
512 19           }
513             #define mmap_impl(var, length, prot, flags, fd, offset, utf8) S_mmap_impl(aTHX_ var, length, prot, flags, fd, offset, utf8)
514              
515             static const map mappable = {
516             { STR_WITH_LEN("unix"), 1 },
517             { STR_WITH_LEN("perlio"), 1 },
518             { STR_WITH_LEN("crlf"), 1 },
519             { STR_WITH_LEN("stdio"), 1 },
520             { STR_WITH_LEN("flock"), 1 },
521             { STR_WITH_LEN("creat"), 1 },
522             { STR_WITH_LEN("mmap"), 1 },
523             };
524              
525             static int S_map_get(pTHX_ const map table, size_t table_size, const char* name, int fallback) {
526             int i;
527 51 50         for (i = 0; i < table_size; ++i) {
    50          
528 51 100         if (strEQ(name, table[i].key))
    100          
529 36           return table[i].value;
530             }
531             return fallback;
532             }
533             #define map_get(table, name, default) S_map_get(aTHX_ table, sizeof table / sizeof *table, name, default)
534              
535 15           int S_check_layers(pTHX_ PerlIO* fh) {
536             PerlIO* current;
537 15 100         if (PerlIO_fileno(fh) < 0)
538 1           Perl_croak(aTHX_ "Can't map fake filehandle");
539 42 100         for (current = fh; *current; current = PerlIONext(current)) {
540 56 50         if (!map_get(mappable, (*current)->tab->name, 0) || (*current)->flags & PERLIO_F_CRLF)
    50          
541 0           Perl_croak(aTHX_ "Shouldn't map non-binary filehandle");
542             }
543 14           return (*fh)->flags & PERLIO_F_UTF8;
544             }
545             #define check_layers(fh) S_check_layers(aTHX_ fh)
546              
547 16           size_t S_get_length(pTHX_ PerlIO* fh, Off_t offset, SV* length_sv) {
548             Stat_t info;
549 16           Fstat(PerlIO_fileno(fh), &info);
550 16 100         size_t length = SvOK(length_sv) ? SvIV(length_sv) : info.st_size - offset;
    50          
    50          
    50          
551 16           size_t end = offset + length;
552 16 100         if (offset < 0 || end > info.st_size && !S_ISCHR(info.st_mode))
    50          
    0          
553 1           Perl_croak(aTHX_ "Window (%ld,%lu) is outside the file", offset, length);
554 15           return length;
555             }
556             #define get_length(fh, offset, length) S_get_length(aTHX_ fh, offset, length)
557              
558             #define READONLY sv_2mortal(newSVpvs("<"))
559             #define undef &PL_sv_undef
560              
561 16           void S_map_handle(pTHX_ SV* var, PerlIO* fh, SV* mode, Off_t offset, SV* length_sv) {
562 16           size_t length = get_length(fh, offset, length_sv);
563 15           int utf8 = check_layers(fh);
564 14           mmap_impl(var, length, protection_sv(mode), MAP_SHARED | MAP_FILE, PerlIO_fileno(fh), offset, utf8);
565 13           }
566             #define map_handle(var, fh, mode, offset, length) S_map_handle(aTHX_ var, fh, mode, offset, length)
567              
568 8           void S_map_file(pTHX_ SV* var, SV* filename, SV* mode, Off_t offset, SV* length_sv) {
569             STRLEN mode_len;
570 8 50         const char* mode_raw = SvPV(mode, mode_len);
571 8 100         if (memchr(mode_raw, ':', mode_len) == NULL) {
572 7           SV* newmode = sv_2mortal(newSVsv(mode));
573 7           sv_catpvs(newmode, ":raw");
574 7 50         mode_raw = SvPV(newmode, mode_len);
575             }
576 8           GV* gv = MUTABLE_GV(sv_2mortal(newSV_type(SVt_NULL)));
577 8           gv_init_pvn(gv, CopSTASH(PL_curcop), "__ANONIO__", 10, GV_ADDMULTI);
578 8 100         if (!do_openn(gv, mode_raw, mode_len, 0, 0, 0, NULL, &filename, 1))
579 1 50         Perl_croak(aTHX_ "Couldn't open file %s: %s", SvPV_nolen(filename), strerror(errno));
580 7 50         map_handle(var, IoIFP(GvIO(gv)), mode, offset, length_sv);
    50          
    50          
581 6           }
582             #define map_file(var, filename, mode, offset, length) S_map_file(aTHX_ var, filename, mode, offset, length)
583              
584             static const map flags = {
585             { STR_WITH_LEN("shared") , MAP_SHARED },
586             { STR_WITH_LEN("private"), MAP_PRIVATE },
587             };
588              
589 16           void S_map_anonymous(pTHX_ SV* var, size_t length, const char* flag_name) {
590             int flag = map_get(flags, flag_name, -1);
591 8 50         if (flag == -1)
592 0           Perl_croak(aTHX_ "No such flag '%s'", flag_name);
593 8 100         if (length == 0)
594 1           Perl_croak(aTHX_ "Zero length specified for anonymous map");
595 7           mmap_impl(var, length, PROT_READ | PROT_WRITE, flag | MAP_ANONYMOUS, -1, 0, 0);
596 6           }
597             #define map_anonymous(var, length, flag_name) S_map_anonymous(aTHX_ var, length, flag_name)
598              
599 0           void S_sys_map(pTHX_ SV* var, size_t length, int protection, int flags, SV* fh, Off_t offset) {
600 0 0         if (flags & MAP_ANONYMOUS)
601 0           mmap_impl(var, length, protection, flags, -1, offset, 0);
602             else {
603 0           PerlIO* pio = IoIFP(sv_2io(fh)); // XXX error check
604 0           int utf8 = check_layers(pio);
605 0           int fd = PerlIO_fileno(pio);
606 0           mmap_impl(var, length, protection, flags, fd, offset, utf8);
607             }
608 0           }
609             #define sys_map(var, length, protection, flags, fh, offset) S_sys_map(aTHX_ var, length, protection, flags, fh, offset)
610              
611 2           void S_sync(pTHX_ SV* var, SV* sync) {
612 2           struct mmap_info* info = get_mmap_magic(aTHX_ var, "sync");
613 1 50         if (EMPTY_MAP(info))
614             return;
615 0 0         if (SvREADONLY(var) && ckWARN(WARN_IO))
    0          
616 0           Perl_warn(aTHX_ "Syncing a readonly map makes no sense");
617 0 0         if (msync(info->real_address, info->real_length, SvTRUE(sync) ? MS_SYNC : MS_ASYNC ) == -1)
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
618 0           die_sys("Could not sync: %s");
619             }
620             #define sync(var, sync) S_sync(aTHX_ var, sync)
621              
622             #ifdef __linux__
623 2           void S_remap(pTHX_ SV* var, size_t new_size) {
624 2           struct mmap_info* info = get_mmap_magic(aTHX_ var, "remap");
625 2           ptrdiff_t correction = info->real_length - info->fake_length;
626             void* new_address;
627             CODE:
628             #ifdef USE_ITHREADS
629             if (info->count != 1)
630             Perl_croak(aTHX_ "Can't remap a shared mapping");
631             #endif
632 2 50         if (EMPTY_MAP(info))
633 0           Perl_croak(aTHX_ "Can't remap empty map"); /* XXX */
634 2 50         if (new_size == 0)
635 0           Perl_croak(aTHX_ "Can't remap to zero");
636 2 50         if ((info->flags & (MAP_ANONYMOUS|MAP_SHARED)) == (MAP_ANONYMOUS|MAP_SHARED))
637 0           Perl_croak(aTHX_ "Can't remap a shared anonymous mapping");
638 2 50         if ((new_address = mremap(info->real_address, info->real_length, new_size + correction, MREMAP_MAYMOVE)) == MAP_FAILED)
639 0           die_sys("Could not remap: %s");
640             set_mmap_info(info, new_address, new_size, correction);
641             reset_var(var, info);
642 2           }
643             #define remap(var, new_size) S_remap(aTHX_ var, new_size)
644             #endif
645              
646 2           void S_unmap(pTHX_ SV* var) {
647 2           get_mmap_magic(aTHX_ var, "unmap");
648 1           sv_unmagicext(var, PERL_MAGIC_ext, (MGVTBL*)&mmap_table);
649 1           }
650             #define unmap(var) S_unmap(aTHX_ var)
651              
652 0           void S_pin(pTHX_ SV* var) {
653 0           struct mmap_info* info = get_mmap_magic(aTHX_ var, "pin");
654             #ifndef VMS
655 0 0         if (EMPTY_MAP(info))
656             return;
657 0 0         if (mlock(info->real_address, info->real_length) == -1)
658 0           die_sys("Could not pin: %s");
659             #else
660             Perl_croak(aTHX_ "pin not implemented on VMS");
661             #endif
662             }
663             #define pin(var) S_pin(aTHX_ var)
664              
665 0           void S_unpin(pTHX_ SV* var) {
666 0           struct mmap_info* info = get_mmap_magic(aTHX_ var, "unpin");
667             #ifndef VMS
668 0 0         if (EMPTY_MAP(info))
669             return;
670 0 0         if (munlock(info->real_address, info->real_length) == -1)
671 0           die_sys("Could not unpin: %s");
672             #else
673             Perl_croak(aTHX_ "unpin not implemented on VMS");
674             #endif
675             }
676             #define unpin(var) S_unpin(aTHX_ var)
677              
678 3           void S_advise(pTHX_ SV* var, SV* name) {
679 3           struct mmap_info* info = get_mmap_magic(aTHX_ var, "advise");
680              
681 3           HV* constants = (HV*) *hv_fetch(PL_modglobal, "File::Map::ADVISE_CONSTANTS", 27, 0);
682 3           HE* value = hv_fetch_ent(constants, name, 0, 0);
683              
684 3 50         if (EMPTY_MAP(info))
685             return;
686 3 100         if (!value) {
687 1 50         if (ckWARN(WARN_PORTABLE))
688 1 50         Perl_warn(aTHX_ "Unknown advice '%s'", SvPV_nolen(name));
689             }
690 2 50         else if (madvise(info->real_address, info->real_length, SvUV(HeVAL(value))) == -1)
    50          
691 0           die_sys("Could not advice: %s");
692             }
693             #define advise(var, name) S_advise(aTHX_ var, name)
694              
695 2           void S_protect(pTHX_ SV* var, SV* prot) {
696 2           struct mmap_info* info = get_mmap_magic(aTHX_ var, "protect");
697 2 50         int prot_val = SvIOK(prot) ? SvIV(prot) : protection_sv(prot);
    0          
698 2 50         if (!EMPTY_MAP(info))
699 2           mprotect(info->real_address, info->real_length, prot_val);
700 2 100         if (prot_val & PROT_WRITE)
701 1           SvREADONLY_off(var);
702             else
703 1           SvREADONLY_on(var);
704 2           }
705             #define protect(var, prot) S_protect(aTHX_ var, prot)
706              
707 1           void S_lock_map(pTHX_ SV* var) {
708 1           struct mmap_info* info = get_mmap_magic(aTHX_ var, "lock_map");
709             #ifdef USE_ITHREADS
710             LEAVE;
711             SAVEDESTRUCTOR_X(magic_end, info);
712             MUTEX_LOCK(&info->data_mutex);
713             info->owner = aTHX;
714             ENTER;
715             #endif
716 0           }
717             #define lock_map(var) S_lock_map(aTHX_ var)
718              
719             #ifdef USE_ITHREADS
720             SV* S_wait_until(pTHX_ SV* block, SV* var) {
721             struct mmap_info* info = get_mmap_magic(aTHX_ var, "wait_until");
722             if (info->owner != aTHX)
723             Perl_croak(aTHX_ "Trying to wait on an unlocked map");
724             SAVESPTR(DEFSV);
725             DEFSV = var;
726             dSP;
727             while (1) {
728             PUSHMARK(SP);
729             call_sv(block, G_SCALAR | G_NOARGS);
730             SPAGAIN;
731             SV* result = POPs;
732             if (SvTRUE(result))
733             return SvREFCNT_inc(result);
734             COND_WAIT(&info->cond, &info->data_mutex);
735             }
736             }
737             #define wait_until(block, var) S_wait_until(aTHX_ block, var)
738              
739             void S_notify(pTHX_ SV* var) {
740             struct mmap_info* info = get_mmap_magic(aTHX_ var, "notify");
741             if (info->owner != aTHX)
742             Perl_croak(aTHX_ "Trying to notify on an unlocked map");
743             COND_SIGNAL(&info->cond);
744             }
745             #define notify(var) S_notify(aTHX_ var)
746              
747             void S_broadcast(pTHX_ SV* var) {
748             struct mmap_info* info = get_mmap_magic(aTHX_ var, "broadcast");
749             if (info->owner != aTHX)
750             Perl_croak(aTHX_ "Trying to broadcast on an unlocked map");
751             COND_BROADCAST(&info->cond);
752             }
753             #define broadcast(var) S_broadcast(aTHX_ var)
754             #endif
755              
756             MODULE = File::Map PACKAGE = File::Map
757              
758             PROTOTYPES: DISABLED
759              
760             BOOT:
761 7           boot(aTHX);
762              
763             void map_file(SV* var, SV* filename, SV* mode = READONLY, Off_t offset = 0, SV* length = undef)
764              
765             void map_handle(SV* var, PerlIO* fh, SV* mode = READONLY, Off_t offset = 0, SV* length = undef)
766              
767             void map_anonymous(SV* var, size_t length, const char* flag_name = "shared")
768              
769             void sys_map(SV* var, size_t length, int protection, int flags, SV* fh = undef, Off_t offset = 0)
770              
771             void sync(SV* var, SV* sync = YES)
772              
773             #ifdef __linux__
774             void remap(SV* var, size_t new_size)
775              
776             #endif
777              
778             void unmap(SV* var)
779              
780             void pin(SV* var)
781              
782             void unpin(SV* var)
783              
784             void advise(SV* var, SV* name)
785              
786             void protect(SV* var, SV* prot)
787              
788             void lock_map(SV* var)
789              
790             #ifdef USE_ITHREADS
791             SV* wait_until(SV* block, SV* var)
792             PROTOTYPE: &@
793              
794             void notify(SV* var)
795              
796             void broadcast(SV* var)
797              
798             #endif /* USE ITHREADS */