File Coverage

Mmap.xs
Criterion Covered Total %
statement 112 149 75.1
branch 64 100 64.0
condition n/a
subroutine n/a
pod n/a
total 176 249 70.6


line stmt bran cond sub pod time code
1             #ifdef __cplusplus
2             extern "C" {
3             #endif
4             #include "EXTERN.h"
5             #include "perl.h"
6             #include "XSUB.h"
7             #include
8             #ifdef __cplusplus
9             }
10             #endif
11             #include
12             #include
13              
14             #ifndef MMAP_RETTYPE
15             #ifndef _POSIX_C_SOURCE
16             #define _POSIX_C_SOURCE 199309
17             #endif
18             #ifdef _POSIX_VERSION
19             #if _POSIX_VERSION >= 199309
20             #define MMAP_RETTYPE void *
21             #endif
22             #endif
23             #endif
24              
25             #ifndef MMAP_RETTYPE
26             #define MMAP_RETTYPE caddr_t
27             #endif
28              
29             #ifndef MAP_FAILED
30             #define MAP_FAILED ((caddr_t)-1)
31             #endif
32              
33             static int
34 0           not_here(s)
35             char *s;
36             {
37 0           croak("%s not implemented on this architecture", s);
38             return -1;
39             }
40              
41             static double
42 25           constant(name, arg)
43             char *name;
44             int arg;
45             {
46 25           errno = 0;
47 25           switch (*name) {
48 14           case 'M':
49 14 100         if (strEQ(name, "MAP_ANON"))
50             #ifdef MAP_ANON
51 2           return MAP_ANON;
52             #else
53             goto not_there;
54             #endif
55 12 50         if (strEQ(name, "MAP_ANONYMOUS"))
56             #ifdef MAP_ANONYMOUS
57 0           return MAP_ANONYMOUS;
58             #else
59             goto not_there;
60             #endif
61 12 100         if (strEQ(name, "MAP_FILE"))
62             #ifdef MAP_FILE
63 2           return MAP_FILE;
64             #else
65             goto not_there;
66             #endif
67 10 50         if (strEQ(name, "MAP_PRIVATE"))
68             #ifdef MAP_PRIVATE
69 0           return MAP_PRIVATE;
70             #else
71             goto not_there;
72             #endif
73 10 100         if (strEQ(name, "MAP_SHARED"))
74             #ifdef MAP_SHARED
75 6           return MAP_SHARED;
76             #else
77             goto not_there;
78             #endif
79 4 50         if (strEQ(name, "MAP_LOCKED"))
80             #ifdef MAP_LOCKED
81 0           return MAP_LOCKED;
82             #else
83             goto not_there;
84             #endif
85 4 50         if (strEQ(name, "MAP_NORESERVE"))
86             #ifdef MAP_NORESERVE
87 0           return MAP_NORESERVE;
88             #else
89             goto not_there;
90             #endif
91 4 100         if (strEQ(name, "MAP_POPULATE"))
92             #ifdef MAP_POPULATE
93 1           return MAP_POPULATE;
94             #else
95             goto not_there;
96             #endif
97 3 50         if (strEQ(name, "MAP_HUGETLB"))
98             #ifdef MAP_HUGETLB
99 0           return MAP_HUGETLB;
100             #else
101             goto not_there;
102             #endif
103 3 50         if (strEQ(name, "MAP_HUGE_2MB"))
104             #ifdef MAP_HUGE_2MB
105             return MAP_HUGE_2MB;
106             #else
107 0           goto not_there;
108             #endif
109 3 50         if (strEQ(name, "MAP_HUGE_1GB"))
110             #ifdef MAP_HUGE_1GB
111             return MAP_HUGE_1GB;
112             #else
113 0           goto not_there;
114             #endif
115 3           break;
116 11           case 'P':
117 11 50         if (strEQ(name, "PROT_EXEC"))
118             #ifdef PROT_EXEC
119 0           return PROT_EXEC;
120             #else
121             goto not_there;
122             #endif
123 11 50         if (strEQ(name, "PROT_NONE"))
124             #ifdef PROT_NONE
125 0           return PROT_NONE;
126             #else
127             goto not_there;
128             #endif
129 11 100         if (strEQ(name, "PROT_READ"))
130             #ifdef PROT_READ
131 6           return PROT_READ;
132             #else
133             goto not_there;
134             #endif
135 5 50         if (strEQ(name, "PROT_WRITE"))
136             #ifdef PROT_WRITE
137 5           return PROT_WRITE;
138             #else
139             goto not_there;
140             #endif
141 0           break;
142 0           default:
143 0           break;
144             }
145 3           errno = EINVAL;
146 3           return 0;
147              
148 0           not_there:
149 0           errno = ENOENT;
150 0           return 0;
151             }
152              
153             static size_t pagesize = 0;
154              
155             /* Magic structure to track mmap info for proper cleanup */
156             typedef struct {
157             void *base_addr; /* actual address returned by mmap() */
158             size_t total_len; /* actual length passed to mmap() (len + slop) */
159             } mmap_info_t;
160              
161             #define MMAP_MAGIC_TYPE PERL_MAGIC_ext
162              
163 15           static int mmap_magic_free(pTHX_ SV *sv, MAGIC *mg) {
164 15           mmap_info_t *info = (mmap_info_t *) mg->mg_ptr;
165 15 50         if (info) {
166 15 100         if (info->base_addr) {
167 2           munmap((MMAP_RETTYPE) info->base_addr, info->total_len);
168 2           info->base_addr = NULL;
169             }
170 15           Safefree(info);
171 15           mg->mg_ptr = NULL;
172             }
173 15           return 0;
174             }
175              
176             static MGVTBL mmap_magic_vtbl = {
177             0, /* get */
178             0, /* set */
179             0, /* len */
180             0, /* clear */
181             mmap_magic_free, /* free */
182             0, /* copy */
183             0, /* dup */
184             0 /* local */
185             };
186              
187             /* Find our mmap magic on an SV, or NULL if not present */
188 17           static MAGIC *find_mmap_magic(SV *sv) {
189             MAGIC *mg;
190 17 100         if (SvTYPE(sv) >= SVt_PVMG) {
191 13 50         for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) {
192 13 50         if (mg->mg_type == MMAP_MAGIC_TYPE && mg->mg_virtual == &mmap_magic_vtbl)
    50          
193 13           return mg;
194             }
195             }
196 4           return NULL;
197             }
198              
199             #if _FILE_OFFSET_BITS > 32
200             #define get_off(a) (atoll(a))
201             #else
202             #define get_off(a) (atoi(a))
203             #endif
204              
205              
206             MODULE = Sys::Mmap PACKAGE = Sys::Mmap
207              
208              
209             double
210             constant(name,arg)
211             char * name
212             int arg
213              
214             SV *
215             hardwire(var, addr, len)
216             SV * var
217             IV addr
218             size_t len
219             PROTOTYPE: $$$
220             CODE:
221 0           ST(0) = &PL_sv_undef;
222 0 0         SvUPGRADE(var, SVt_PV);
223 0           SvPVX(var) = (char *) addr;
224 0           SvCUR_set(var, len);
225 0           SvLEN_set(var, 0);
226 0           SvPOK_only(var);
227             /*printf("ok, that var is now stuck at addr %lx\n", addr);*/
228 0           ST(0) = &PL_sv_yes;
229              
230              
231              
232             SV *
233             mmap(var, len, prot, flags, fh = 0, off_string)
234             SV * var
235             size_t len
236             int prot
237             int flags
238             FILE * fh
239             SV * off_string
240             int fd = NO_INIT
241             MMAP_RETTYPE addr = NO_INIT
242             off_t slop = NO_INIT
243             off_t off = NO_INIT
244             PROTOTYPE: $$$$*;$
245             CODE:
246              
247 19 50         if(!SvTRUE(off_string)) {
248 0           off = 0;
249             }
250             else {
251 19           off = get_off(SvPVbyte_nolen(off_string));
252             }
253            
254 19 100         if(off < 0) {
255 1           croak("mmap: Cannot operate on a negative offset (%s) ", SvPVbyte_nolen(off_string));
256             }
257            
258 18           ST(0) = &PL_sv_undef;
259 18 100         if(flags&MAP_ANON) {
260 3           fd = -1;
261 3 100         if (!len) {
262             /* i WANT to return undef and set $! but perlxs and perlxstut dont tell me how... waa! */
263 1           croak("mmap: MAP_ANON specified, but no length specified. cannot infer length from file");
264             }
265             } else {
266 15           fd = fileno(fh);
267 15 50         if (fd < 0) {
268 0           croak("mmap: file not open or does not have associated fileno");
269             }
270 15 100         if (!len) {
271             struct stat st;
272 9 50         if (fstat(fd, &st) == -1) {
273 0           croak("mmap: no len provided, fstat failed, unable to infer length");
274             }
275 9 100         if (off >= st.st_size) {
276 2           croak("mmap: offset (%"IVdf") is at or beyond end of file (size %"IVdf")", (IV)off, (IV)st.st_size);
277             }
278 7           len = st.st_size - off;
279             }
280             }
281              
282 15 100         if (pagesize == 0) {
283 3           pagesize = getpagesize();
284             }
285              
286 15           slop = (size_t) off % pagesize;
287              
288 15           addr = mmap(0, len + slop, prot, flags, fd, off - slop);
289 15 50         if (addr == MAP_FAILED) {
290 0           croak("mmap: mmap call failed: errno: %d errmsg: %s ", errno, strerror(errno));
291             }
292             #if PERL_VERSION >= 20
293              
294 15 50         if (SvIsCOW(var)) {
295 0           sv_force_normal_flags(var, 0);
296             }
297             #endif
298              
299 15 100         SvUPGRADE(var, SVt_PV);
300 15 100         if (!(prot & PROT_WRITE))
301 9           SvREADONLY_on(var);
302              
303             /* would sv_usepvn() be cleaner/better/different? would still try to realloc... */
304 15           SvPVX(var) = (char *) addr + slop;
305 15           SvCUR_set(var, len);
306 15           SvLEN_set(var, 0); /* must be 0 so Perl won't Safefree() the mmap'd pointer */
307 15           SvPOK_only(var);
308              
309             /* Attach magic to handle munmap on cleanup */
310             {
311             mmap_info_t *info;
312             MAGIC *mg;
313 15           Newxz(info, 1, mmap_info_t);
314 15           info->base_addr = (void *) addr;
315 15           info->total_len = len + slop;
316 15           mg = sv_magicext(var, NULL, MMAP_MAGIC_TYPE, &mmap_magic_vtbl,
317             (const char *) info, 0);
318 15           mg->mg_flags |= MGf_LOCAL;
319             }
320              
321 15           ST(0) = sv_2mortal(newSVnv((IV) addr));
322              
323             SV *
324             munmap(var)
325             SV * var
326             PROTOTYPE: $
327             CODE:
328 24           ST(0) = &PL_sv_undef;
329             /* XXX refrain from dumping core if this var wasnt previously mmap'd */
330 24 100         if(!SvOK(var)) { /* Detect if variable is undef */
331 3           croak("undef variable not unmappable");
332             return;
333             }
334 21 100         if(SvTYPE(var) < SVt_PV || SvTYPE(var) > SVt_PVMG) {
    50          
335 7           croak("variable is not a string, type is: %d", SvTYPE(var));
336             return;
337             }
338              
339             {
340 14           MAGIC *mg = find_mmap_magic(var);
341 14 100         if (mg) {
342 10           mmap_info_t *info = (mmap_info_t *) mg->mg_ptr;
343 10 50         if (munmap((MMAP_RETTYPE) info->base_addr, info->total_len) == -1) {
344 0           croak("munmap failed! errno %d %s\n", errno, strerror(errno));
345             return;
346             }
347 10           info->base_addr = NULL; /* prevent double munmap in magic free */
348             } else {
349             /* fallback for hardwire'd or legacy variables without magic */
350             /* SvLEN > 0 means this is a regular Perl string, not mmap'd */
351 4 50         if (SvLEN(var) != 0) {
352 4           errno = EINVAL;
353 4           croak("munmap failed! errno %d %s\n", errno, strerror(errno));
354             return;
355             }
356 0 0         if (munmap((MMAP_RETTYPE) SvPVX(var), SvCUR(var)) == -1) {
357 0           croak("munmap failed! errno %d %s\n", errno, strerror(errno));
358             return;
359             }
360             }
361             }
362 10           SvREADONLY_off(var);
363 10           SvPVX(var) = 0;
364 10           SvCUR_set(var, 0);
365 10           SvLEN_set(var, 0);
366 10 50         SvOK_off(var);
367 10           ST(0) = &PL_sv_yes;
368              
369             void
370             DESTROY(var)
371             SV * var
372             PROTOTYPE: $
373             CODE:
374             /* XXX refrain from dumping core if this var wasnt previously mmap'd*/
375              
376             /* For tied objects: DESTROY receives the blessed reference (\$mmap_sv),
377             * not the mmap'd SV itself. Dereference to reach the actual mapping. */
378 3 50         if (SvROK(var))
379 3           var = SvRV(var);
380              
381             {
382 3           MAGIC *mg = find_mmap_magic(var);
383 3 50         if (mg) {
384 3           mmap_info_t *info = (mmap_info_t *) mg->mg_ptr;
385 3 50         if (info->base_addr) {
386 3 50         if (munmap((MMAP_RETTYPE) info->base_addr, info->total_len) == -1) {
387 0           croak("munmap failed! errno %d %s\n", errno, strerror(errno));
388             return;
389             }
390 3           info->base_addr = NULL;
391             }
392             } else {
393             /* SvLEN > 0 means this is a regular Perl string, not mmap'd */
394 0 0         if (SvLEN(var) != 0)
395 0           return;
396 0 0         if (munmap((MMAP_RETTYPE) SvPVX(var), SvCUR(var)) == -1) {
397 0           croak("munmap failed! errno %d %s\n", errno, strerror(errno));
398             return;
399             }
400             }
401             }
402 3           SvREADONLY_off(var);
403 3           SvPVX(var) = 0;
404 3           SvCUR_set(var, 0);
405 3           SvLEN_set(var, 0);
406 3 50         SvOK_off(var);
407             /* printf("destroy ran fine, thanks\n"); */
408 3           ST(0) = &PL_sv_yes;