4 * Id: text_mmap.c,v 4.15 2006/11/27 01:52:23 bkorb Exp
6 * Time-stamp: "2006-09-10 14:50:04 bkorb"
11 # define MAP_ANONYMOUS MAP_ANON
16 * Some weird systems require that a specifically invalid FD number
17 * get passed in as an argument value. Which value is that? Well,
18 * as everybody knows, if open(2) fails, it returns -1, so that must
21 #define AO_INVALID_FD -1
23 #define FILE_WRITABLE(_prt,_flg) \
24 ( (_prt & PROT_WRITE) \
25 && ((_flg & (MAP_SHARED|MAP_PRIVATE)) == MAP_SHARED))
26 #define MAP_FAILED_PTR ((void*)MAP_FAILED)
28 /*=export_func text_mmap
31 * what: map a text file with terminating NUL
33 * arg: char const*, pzFile, name of the file to map
34 * arg: int, prot, mmap protections (see mmap(2))
35 * arg: int, flags, mmap flags (see mmap(2))
36 * arg: tmap_info_t*, mapinfo, returned info about the mapping
39 * ret-desc: The mmaped data address
43 * This routine will mmap a file into memory ensuring that there is at least
44 * one @file{NUL} character following the file data. It will return the
45 * address where the file contents have been mapped into memory. If there is a
46 * problem, then it will return @code{MAP_FAILED} and set @file{errno}
49 * The named file does not exist, @code{stat(2)} will set @file{errno} as it
50 * will. If the file is not a regular file, @file{errno} will be
51 * @code{EINVAL}. At that point, @code{open(2)} is attempted with the access
52 * bits set appropriately for the requested @code{mmap(2)} protections and flag
53 * bits. On failure, @file{errno} will be set according to the documentation
54 * for @code{open(2)}. If @code{mmap(2)} fails, @file{errno} will be set as
55 * that routine sets it. If @code{text_mmap} works to this point, a valid
56 * address will be returned, but there may still be ``issues''.
58 * If the file size is not an even multiple of the system page size, then
59 * @code{text_map} will return at this point and @file{errno} will be zero.
60 * Otherwise, an anonymous map is attempted. If not available, then an attempt
61 * is made to @code{mmap(2)} @file{/dev/zero}. If any of these fail, the
62 * address of the file's data is returned, bug @code{no} @file{NUL} characters
63 * are mapped after the end of the data.
65 * see: mmap(2), open(2), stat(2)
67 * err: Any error code issued by mmap(2), open(2), stat(2) is possible.
68 * Additionally, if the specified file is not a regular file, then
69 * errno will be set to @code{EINVAL}.
75 * void* data = text_mmap( "file", PROT_WRITE, MAP_PRIVATE, &mi );
76 * if (data == MAP_FAILED) return;
77 * no_nul = (mi.txt_size == mi.txt_full_size);
82 text_mmap( char const* pzFile
, int prot
, int flags
, tmap_info_t
* pMI
)
84 memset( pMI
, 0, sizeof(*pMI
) );
86 pMI
->txt_zero_fd
= -1;
91 * Make sure we can stat the regular file. Save the file size.
95 if (stat( pzFile
, &sb
) != 0) {
96 pMI
->txt_errno
= errno
;
97 return MAP_FAILED_PTR
;
100 if (! S_ISREG( sb
.st_mode
)) {
101 pMI
->txt_errno
= errno
= EINVAL
;
102 return MAP_FAILED_PTR
;
105 pMI
->txt_size
= sb
.st_size
;
109 * Map mmap flags and protections into open flags and do the open.
114 * See if we will be updating the file. If we can alter the memory
115 * and if we share the data and we are *not* copy-on-writing the data,
116 * then our updates will show in the file, so we must open with
119 if (FILE_WRITABLE(prot
,flags
))
125 * If you're not sharing the file and you are writing to it,
126 * then don't let anyone else have access to the file.
128 if (((flags
& MAP_SHARED
) == 0) && (prot
& PROT_WRITE
))
131 pMI
->txt_fd
= open( pzFile
, o_flag
);
134 if (pMI
->txt_fd
== AO_INVALID_FD
) {
135 pMI
->txt_errno
= errno
;
136 return MAP_FAILED_PTR
;
139 #ifdef HAVE_MMAP /* * * * * WITH MMAP * * * * * */
141 * do the mmap. If we fail, then preserve errno, close the file and
142 * return the failure.
145 mmap(NULL
, pMI
->txt_size
+1, prot
, flags
, pMI
->txt_fd
, (size_t)0);
146 if (pMI
->txt_data
== MAP_FAILED_PTR
) {
147 pMI
->txt_errno
= errno
;
152 * Most likely, everything will turn out fine now. The only difficult
153 * part at this point is coping with files with sizes that are a multiple
154 * of the page size. Handling that is what this whole thing is about.
156 pMI
->txt_zero_fd
= -1;
162 size_t pgsz
= sysconf(_SC_PAGESIZE
);
164 size_t pgsz
= getpagesize();
167 * Compute the pagesize rounded mapped memory size.
168 * IF this is not the same as the file size, then there are NUL's
169 * at the end of the file mapping and all is okay.
171 pMI
->txt_full_size
= (pMI
->txt_size
+ (pgsz
- 1)) & ~(pgsz
- 1);
172 if (pMI
->txt_size
!= pMI
->txt_full_size
)
173 return pMI
->txt_data
;
176 * Still here? We have to remap the trailing inaccessible page
177 * either anonymously or to /dev/zero.
179 pMI
->txt_full_size
+= pgsz
;
180 #if defined(MAP_ANONYMOUS)
182 (void*)(((char*)pMI
->txt_data
) + pMI
->txt_size
),
183 pgsz
, PROT_READ
|PROT_WRITE
,
184 MAP_ANONYMOUS
|MAP_FIXED
|MAP_PRIVATE
, AO_INVALID_FD
, (size_t)0);
186 if (pNuls
!= MAP_FAILED_PTR
)
187 return pMI
->txt_data
;
189 pMI
->txt_errno
= errno
;
191 #elif defined(HAVE_DEV_ZERO)
192 pMI
->txt_zero_fd
= open( "/dev/zero", O_RDONLY
);
194 if (pMI
->txt_zero_fd
== AO_INVALID_FD
) {
195 pMI
->txt_errno
= errno
;
199 (void*)(((char*)pMI
->txt_data
) + pMI
->txt_size
), pgsz
,
200 PROT_READ
|PROT_WRITE
, MAP_PRIVATE
|MAP_FIXED
,
201 pMI
->txt_zero_fd
, 0 );
203 if (pNuls
!= MAP_FAILED_PTR
)
204 return pMI
->txt_data
;
206 pMI
->txt_errno
= errno
;
207 close( pMI
->txt_zero_fd
);
208 pMI
->txt_zero_fd
= -1;
212 pMI
->txt_full_size
= pMI
->txt_size
;
216 void* p
= AGALOC( pMI
->txt_size
+1, "file text" );
217 memcpy( p
, pMI
->txt_data
, pMI
->txt_size
);
218 ((char*)p
)[pMI
->txt_size
] = NUL
;
219 munmap(pMI
->txt_data
, pMI
->txt_size
);
223 return pMI
->txt_data
;
225 #else /* * * * * * no HAVE_MMAP * * * * * */
227 pMI
->txt_data
= AGALOC( pMI
->txt_size
+1, "file text" );
228 if (pMI
->txt_data
== NULL
) {
229 pMI
->txt_errno
= ENOMEM
;
234 size_t sz
= pMI
->txt_size
;
235 char* pz
= pMI
->txt_data
;
238 ssize_t rdct
= read( pMI
->txt_fd
, pz
, sz
);
240 pMI
->txt_errno
= errno
;
241 fprintf( stderr
, zFSErrReadFile
,
242 errno
, strerror( errno
), pzFile
);
243 free( pMI
->txt_data
);
255 * We never need a dummy page mapped in
257 pMI
->txt_zero_fd
= -1;
260 return pMI
->txt_data
;
262 #endif /* * * * * * no HAVE_MMAP * * * * * */
265 if (pMI
->txt_fd
>= 0) {
266 close( pMI
->txt_fd
);
269 errno
= pMI
->txt_errno
;
270 pMI
->txt_data
= MAP_FAILED_PTR
;
271 return pMI
->txt_data
;
275 /*=export_func text_munmap
278 * what: unmap the data mapped in by text_mmap
280 * arg: tmap_info_t*, mapinfo, info about the mapping
283 * ret-desc: -1 or 0. @file{errno} will have the error code.
287 * This routine will unmap the data mapped in with @code{text_mmap} and close
288 * the associated file descriptors opened by that function.
290 * see: munmap(2), close(2)
292 * err: Any error code issued by munmap(2) or close(2) is possible.
295 text_munmap( tmap_info_t
* pMI
)
299 if (pMI
->txt_alloc
) {
301 * IF the user has write permission and the text is not mapped private,
302 * then write back any changes. Hopefully, nobody else has modified
303 * the file in the mean time.
305 if ( ((pMI
->txt_prot
& PROT_WRITE
) != 0)
306 && ((pMI
->txt_flags
& MAP_PRIVATE
) == 0)) {
308 if (lseek(pMI
->txt_fd
, (size_t)0, SEEK_SET
) != 0)
311 res
= (write( pMI
->txt_fd
, pMI
->txt_data
, pMI
->txt_size
) < 0)
315 AGFREE( pMI
->txt_data
);
318 res
= munmap( pMI
->txt_data
, pMI
->txt_full_size
);
323 res
= close( pMI
->txt_fd
);
329 if (pMI
->txt_zero_fd
!= -1) {
330 res
= close( pMI
->txt_zero_fd
);
331 pMI
->txt_zero_fd
= -1;
335 pMI
->txt_errno
= errno
;
337 #else /* HAVE_MMAP */
341 * IF the memory is writable *AND* it is not private (copy-on-write)
342 * *AND* the memory is "sharable" (seen by other processes)
343 * THEN rewrite the data.
345 if ( FILE_WRITABLE(pMI
->txt_prot
, pMI
->txt_flags
)
346 && (lseek( pMI
->txt_fd
, 0, SEEK_SET
) >= 0) ) {
347 write( pMI
->txt_fd
, pMI
->txt_data
, pMI
->txt_size
);
350 close( pMI
->txt_fd
);
352 pMI
->txt_errno
= errno
;
353 free( pMI
->txt_data
);
355 return pMI
->txt_errno
;
356 #endif /* HAVE_MMAP */
362 * c-file-style: "stroustrup"
363 * indent-tabs-mode: nil
365 * end of autoopts/text_mmap.c */