4 * Id: 14e1f51d1a5a31d8395fdf1e93a07bace1c59e87
6 * Time-stamp: "2007-07-04 11:35:49 bkorb"
8 * This file is part of AutoOpts, a companion to AutoGen.
9 * AutoOpts is free software.
10 * AutoOpts is copyright (c) 1992-2009 by Bruce Korb - all rights reserved
12 * AutoOpts is available under any one of two licenses. The license
13 * in use must be one of these two and the choice is under the control
14 * of the user of the license.
16 * The GNU Lesser General Public License, version 3 or later
17 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
19 * The Modified Berkeley Software Distribution License
20 * See the file "COPYING.mbsd"
22 * These files have the following md5sums:
24 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
25 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
26 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
31 # define MAP_ANONYMOUS MAP_ANON
36 * Some weird systems require that a specifically invalid FD number
37 * get passed in as an argument value. Which value is that? Well,
38 * as everybody knows, if open(2) fails, it returns -1, so that must
41 #define AO_INVALID_FD -1
43 #define FILE_WRITABLE(_prt,_flg) \
44 ( (_prt & PROT_WRITE) \
45 && ((_flg & (MAP_SHARED|MAP_PRIVATE)) == MAP_SHARED))
46 #define MAP_FAILED_PTR ((void*)MAP_FAILED)
48 /*=export_func text_mmap
51 * what: map a text file with terminating NUL
53 * arg: char const*, pzFile, name of the file to map
54 * arg: int, prot, mmap protections (see mmap(2))
55 * arg: int, flags, mmap flags (see mmap(2))
56 * arg: tmap_info_t*, mapinfo, returned info about the mapping
59 * ret-desc: The mmaped data address
63 * This routine will mmap a file into memory ensuring that there is at least
64 * one @file{NUL} character following the file data. It will return the
65 * address where the file contents have been mapped into memory. If there is a
66 * problem, then it will return @code{MAP_FAILED} and set @file{errno}
69 * The named file does not exist, @code{stat(2)} will set @file{errno} as it
70 * will. If the file is not a regular file, @file{errno} will be
71 * @code{EINVAL}. At that point, @code{open(2)} is attempted with the access
72 * bits set appropriately for the requested @code{mmap(2)} protections and flag
73 * bits. On failure, @file{errno} will be set according to the documentation
74 * for @code{open(2)}. If @code{mmap(2)} fails, @file{errno} will be set as
75 * that routine sets it. If @code{text_mmap} works to this point, a valid
76 * address will be returned, but there may still be ``issues''.
78 * If the file size is not an even multiple of the system page size, then
79 * @code{text_map} will return at this point and @file{errno} will be zero.
80 * Otherwise, an anonymous map is attempted. If not available, then an attempt
81 * is made to @code{mmap(2)} @file{/dev/zero}. If any of these fail, the
82 * address of the file's data is returned, bug @code{no} @file{NUL} characters
83 * are mapped after the end of the data.
85 * see: mmap(2), open(2), stat(2)
87 * err: Any error code issued by mmap(2), open(2), stat(2) is possible.
88 * Additionally, if the specified file is not a regular file, then
89 * errno will be set to @code{EINVAL}.
95 * void* data = text_mmap( "file", PROT_WRITE, MAP_PRIVATE, &mi );
96 * if (data == MAP_FAILED) return;
97 * no_nul = (mi.txt_size == mi.txt_full_size);
102 text_mmap( char const* pzFile
, int prot
, int flags
, tmap_info_t
* pMI
)
104 memset( pMI
, 0, sizeof(*pMI
) );
106 pMI
->txt_zero_fd
= -1;
111 * Make sure we can stat the regular file. Save the file size.
115 if (stat( pzFile
, &sb
) != 0) {
116 pMI
->txt_errno
= errno
;
117 return MAP_FAILED_PTR
;
120 if (! S_ISREG( sb
.st_mode
)) {
121 pMI
->txt_errno
= errno
= EINVAL
;
122 return MAP_FAILED_PTR
;
125 pMI
->txt_size
= sb
.st_size
;
129 * Map mmap flags and protections into open flags and do the open.
134 * See if we will be updating the file. If we can alter the memory
135 * and if we share the data and we are *not* copy-on-writing the data,
136 * then our updates will show in the file, so we must open with
139 if (FILE_WRITABLE(prot
,flags
))
145 * If you're not sharing the file and you are writing to it,
146 * then don't let anyone else have access to the file.
148 if (((flags
& MAP_SHARED
) == 0) && (prot
& PROT_WRITE
))
151 pMI
->txt_fd
= open( pzFile
, o_flag
);
154 if (pMI
->txt_fd
== AO_INVALID_FD
) {
155 pMI
->txt_errno
= errno
;
156 return MAP_FAILED_PTR
;
159 #ifdef HAVE_MMAP /* * * * * WITH MMAP * * * * * */
161 * do the mmap. If we fail, then preserve errno, close the file and
162 * return the failure.
165 mmap(NULL
, pMI
->txt_size
+1, prot
, flags
, pMI
->txt_fd
, (size_t)0);
166 if (pMI
->txt_data
== MAP_FAILED_PTR
) {
167 pMI
->txt_errno
= errno
;
172 * Most likely, everything will turn out fine now. The only difficult
173 * part at this point is coping with files with sizes that are a multiple
174 * of the page size. Handling that is what this whole thing is about.
176 pMI
->txt_zero_fd
= -1;
182 size_t pgsz
= sysconf(_SC_PAGESIZE
);
184 size_t pgsz
= getpagesize();
187 * Compute the pagesize rounded mapped memory size.
188 * IF this is not the same as the file size, then there are NUL's
189 * at the end of the file mapping and all is okay.
191 pMI
->txt_full_size
= (pMI
->txt_size
+ (pgsz
- 1)) & ~(pgsz
- 1);
192 if (pMI
->txt_size
!= pMI
->txt_full_size
)
193 return pMI
->txt_data
;
196 * Still here? We have to remap the trailing inaccessible page
197 * either anonymously or to /dev/zero.
199 pMI
->txt_full_size
+= pgsz
;
200 #if defined(MAP_ANONYMOUS)
202 (void*)(((char*)pMI
->txt_data
) + pMI
->txt_size
),
203 pgsz
, PROT_READ
|PROT_WRITE
,
204 MAP_ANONYMOUS
|MAP_FIXED
|MAP_PRIVATE
, AO_INVALID_FD
, (size_t)0);
206 if (pNuls
!= MAP_FAILED_PTR
)
207 return pMI
->txt_data
;
209 pMI
->txt_errno
= errno
;
211 #elif defined(HAVE_DEV_ZERO)
212 pMI
->txt_zero_fd
= open( "/dev/zero", O_RDONLY
);
214 if (pMI
->txt_zero_fd
== AO_INVALID_FD
) {
215 pMI
->txt_errno
= errno
;
219 (void*)(((char*)pMI
->txt_data
) + pMI
->txt_size
), pgsz
,
220 PROT_READ
|PROT_WRITE
, MAP_PRIVATE
|MAP_FIXED
,
221 pMI
->txt_zero_fd
, 0 );
223 if (pNuls
!= MAP_FAILED_PTR
)
224 return pMI
->txt_data
;
226 pMI
->txt_errno
= errno
;
227 close( pMI
->txt_zero_fd
);
228 pMI
->txt_zero_fd
= -1;
232 pMI
->txt_full_size
= pMI
->txt_size
;
236 void* p
= AGALOC( pMI
->txt_size
+1, "file text" );
237 memcpy( p
, pMI
->txt_data
, pMI
->txt_size
);
238 ((char*)p
)[pMI
->txt_size
] = NUL
;
239 munmap(pMI
->txt_data
, pMI
->txt_size
);
243 return pMI
->txt_data
;
245 #else /* * * * * * no HAVE_MMAP * * * * * */
247 pMI
->txt_data
= AGALOC( pMI
->txt_size
+1, "file text" );
248 if (pMI
->txt_data
== NULL
) {
249 pMI
->txt_errno
= ENOMEM
;
254 size_t sz
= pMI
->txt_size
;
255 char* pz
= pMI
->txt_data
;
258 ssize_t rdct
= read( pMI
->txt_fd
, pz
, sz
);
260 pMI
->txt_errno
= errno
;
261 fprintf( stderr
, zFSErrReadFile
,
262 errno
, strerror( errno
), pzFile
);
263 free( pMI
->txt_data
);
275 * We never need a dummy page mapped in
277 pMI
->txt_zero_fd
= -1;
280 return pMI
->txt_data
;
282 #endif /* * * * * * no HAVE_MMAP * * * * * */
285 if (pMI
->txt_fd
>= 0) {
286 close( pMI
->txt_fd
);
289 errno
= pMI
->txt_errno
;
290 pMI
->txt_data
= MAP_FAILED_PTR
;
291 return pMI
->txt_data
;
295 /*=export_func text_munmap
298 * what: unmap the data mapped in by text_mmap
300 * arg: tmap_info_t*, mapinfo, info about the mapping
303 * ret-desc: -1 or 0. @file{errno} will have the error code.
307 * This routine will unmap the data mapped in with @code{text_mmap} and close
308 * the associated file descriptors opened by that function.
310 * see: munmap(2), close(2)
312 * err: Any error code issued by munmap(2) or close(2) is possible.
315 text_munmap( tmap_info_t
* pMI
)
319 if (pMI
->txt_alloc
) {
321 * IF the user has write permission and the text is not mapped private,
322 * then write back any changes. Hopefully, nobody else has modified
323 * the file in the mean time.
325 if ( ((pMI
->txt_prot
& PROT_WRITE
) != 0)
326 && ((pMI
->txt_flags
& MAP_PRIVATE
) == 0)) {
328 if (lseek(pMI
->txt_fd
, (size_t)0, SEEK_SET
) != 0)
331 res
= (write( pMI
->txt_fd
, pMI
->txt_data
, pMI
->txt_size
) < 0)
335 AGFREE( pMI
->txt_data
);
338 res
= munmap( pMI
->txt_data
, pMI
->txt_full_size
);
343 res
= close( pMI
->txt_fd
);
349 if (pMI
->txt_zero_fd
!= -1) {
350 res
= close( pMI
->txt_zero_fd
);
351 pMI
->txt_zero_fd
= -1;
355 pMI
->txt_errno
= errno
;
357 #else /* HAVE_MMAP */
361 * IF the memory is writable *AND* it is not private (copy-on-write)
362 * *AND* the memory is "sharable" (seen by other processes)
363 * THEN rewrite the data.
365 if ( FILE_WRITABLE(pMI
->txt_prot
, pMI
->txt_flags
)
366 && (lseek( pMI
->txt_fd
, 0, SEEK_SET
) >= 0) ) {
367 write( pMI
->txt_fd
, pMI
->txt_data
, pMI
->txt_size
);
370 close( pMI
->txt_fd
);
372 pMI
->txt_errno
= errno
;
373 free( pMI
->txt_data
);
375 return pMI
->txt_errno
;
376 #endif /* HAVE_MMAP */
382 * c-file-style: "stroustrup"
383 * indent-tabs-mode: nil
385 * end of autoopts/text_mmap.c */