~ubuntu-branches/ubuntu/gutsy/ntp/gutsy

« back to all changes in this revision

Viewing changes to libopts/text_mmap.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-05-18 22:41:56 UTC
  • mfrom: (1.2.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20070518224156-563ruqxsxvqvoy8h
Tags: 1:4.2.4p0+dfsg-1ubuntu1
* Merge from Debian unstable.
* Remaining Ubuntu changes:
  - Update version in conflicts/replaces to that which was shipped in edgy,
    which was later than that in Debian (due to the ubuntuX).
  - Change default server to ntp.ubuntu.com.
  - Remove stop links from rc0 and rc6
  - Call dh_installinit with --error-handler
  - Set Ubuntu maintainer address.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * $Id: text_mmap.c,v 4.14 2006/09/24 02:11:16 bkorb Exp $
 
3
 *
 
4
 * Time-stamp:      "2006-09-10 14:50:04 bkorb"
 
5
 */
 
6
 
 
7
#ifndef MAP_ANONYMOUS
 
8
#  ifdef   MAP_ANON
 
9
#  define  MAP_ANONYMOUS   MAP_ANON
 
10
#  endif
 
11
#endif
 
12
 
 
13
/*
 
14
 *  Some weird systems require that a specifically invalid FD number
 
15
 *  get passed in as an argument value.  Which value is that?  Well,
 
16
 *  as everybody knows, if open(2) fails, it returns -1, so that must
 
17
 *  be the value.  :)
 
18
 */
 
19
#define AO_INVALID_FD  -1
 
20
 
 
21
#define FILE_WRITABLE(_prt,_flg) \
 
22
        (   (_prt & PROT_WRITE) \
 
23
         && ((_flg & (MAP_SHARED|MAP_PRIVATE)) == MAP_SHARED))
 
24
#define MAP_FAILED_PTR ((void*)MAP_FAILED)
 
25
 
 
26
/*=export_func  text_mmap
 
27
 * private:
 
28
 *
 
29
 * what:  map a text file with terminating NUL
 
30
 *
 
31
 * arg:   char const*,  pzFile,  name of the file to map
 
32
 * arg:   int,          prot,    mmap protections (see mmap(2))
 
33
 * arg:   int,          flags,   mmap flags (see mmap(2))
 
34
 * arg:   tmap_info_t*, mapinfo, returned info about the mapping
 
35
 *
 
36
 * ret-type:   void*
 
37
 * ret-desc:   The mmaped data address
 
38
 *
 
39
 * doc:
 
40
 *
 
41
 * This routine will mmap a file into memory ensuring that there is at least
 
42
 * one @file{NUL} character following the file data.  It will return the
 
43
 * address where the file contents have been mapped into memory.  If there is a
 
44
 * problem, then it will return @code{MAP_FAILED} and set @file{errno}
 
45
 * appropriately.
 
46
 *
 
47
 * The named file does not exist, @code{stat(2)} will set @file{errno} as it
 
48
 * will.  If the file is not a regular file, @file{errno} will be
 
49
 * @code{EINVAL}.  At that point, @code{open(2)} is attempted with the access
 
50
 * bits set appropriately for the requested @code{mmap(2)} protections and flag
 
51
 * bits.  On failure, @file{errno} will be set according to the documentation
 
52
 * for @code{open(2)}.  If @code{mmap(2)} fails, @file{errno} will be set as
 
53
 * that routine sets it.  If @code{text_mmap} works to this point, a valid
 
54
 * address will be returned, but there may still be ``issues''.
 
55
 *
 
56
 * If the file size is not an even multiple of the system page size, then
 
57
 * @code{text_map} will return at this point and @file{errno} will be zero.
 
58
 * Otherwise, an anonymous map is attempted.  If not available, then an attempt
 
59
 * is made to @code{mmap(2)} @file{/dev/zero}.  If any of these fail, the
 
60
 * address of the file's data is returned, bug @code{no} @file{NUL} characters
 
61
 * are mapped after the end of the data.
 
62
 *
 
63
 * see: mmap(2), open(2), stat(2)
 
64
 *
 
65
 * err: Any error code issued by mmap(2), open(2), stat(2) is possible.
 
66
 *      Additionally, if the specified file is not a regular file, then
 
67
 *      errno will be set to @code{EINVAL}.
 
68
 *
 
69
 * example:
 
70
 * #include <mylib.h>
 
71
 * tmap_info_t mi;
 
72
 * int no_nul;
 
73
 * void* data = text_mmap( "file", PROT_WRITE, MAP_PRIVATE, &mi );
 
74
 * if (data == MAP_FAILED) return;
 
75
 * no_nul = (mi.txt_size == mi.txt_full_size);
 
76
 * << use the data >>
 
77
 * text_munmap( &mi );
 
78
=*/
 
79
void*
 
80
text_mmap( char const* pzFile, int prot, int flags, tmap_info_t* pMI )
 
81
{
 
82
    memset( pMI, 0, sizeof(*pMI) );
 
83
#ifdef HAVE_MMAP
 
84
    pMI->txt_zero_fd = -1;
 
85
#endif
 
86
    pMI->txt_fd = -1;
 
87
 
 
88
    /*
 
89
     *  Make sure we can stat the regular file.  Save the file size.
 
90
     */
 
91
    {
 
92
        struct stat sb;
 
93
        if (stat( pzFile, &sb ) != 0) {
 
94
            pMI->txt_errno = errno;
 
95
            return MAP_FAILED_PTR;
 
96
        }
 
97
 
 
98
        if (! S_ISREG( sb.st_mode )) {
 
99
            pMI->txt_errno = errno = EINVAL;
 
100
            return MAP_FAILED_PTR;
 
101
        }
 
102
 
 
103
        pMI->txt_size = sb.st_size;
 
104
    }
 
105
 
 
106
    /*
 
107
     *  Map mmap flags and protections into open flags and do the open.
 
108
     */
 
109
    {
 
110
        int o_flag;
 
111
        /*
 
112
         *  See if we will be updating the file.  If we can alter the memory
 
113
         *  and if we share the data and we are *not* copy-on-writing the data,
 
114
         *  then our updates will show in the file, so we must open with
 
115
         *  write access.
 
116
         */
 
117
        if (FILE_WRITABLE(prot,flags))
 
118
            o_flag = O_RDWR;
 
119
        else
 
120
            o_flag = O_RDONLY;
 
121
 
 
122
        /*
 
123
         *  If you're not sharing the file and you are writing to it,
 
124
         *  then don't let anyone else have access to the file.
 
125
         */
 
126
        if (((flags & MAP_SHARED) == 0) && (prot & PROT_WRITE))
 
127
            o_flag |= O_EXCL;
 
128
 
 
129
        pMI->txt_fd = open( pzFile, o_flag );
 
130
    }
 
131
 
 
132
    if (pMI->txt_fd == AO_INVALID_FD) {
 
133
        pMI->txt_errno = errno;
 
134
        return MAP_FAILED_PTR;
 
135
    }
 
136
 
 
137
#ifdef HAVE_MMAP /* * * * * WITH MMAP * * * * * */
 
138
    /*
 
139
     *  do the mmap.  If we fail, then preserve errno, close the file and
 
140
     *  return the failure.
 
141
     */
 
142
    pMI->txt_data = mmap( NULL, pMI->txt_size+1, prot, flags, pMI->txt_fd, 0 );
 
143
    if (pMI->txt_data == MAP_FAILED_PTR) {
 
144
        pMI->txt_errno = errno;
 
145
        goto fail_return;
 
146
    }
 
147
 
 
148
    /*
 
149
     *  Most likely, everything will turn out fine now.  The only difficult
 
150
     *  part at this point is coping with files with sizes that are a multiple
 
151
     *  of the page size.  Handling that is what this whole thing is about.
 
152
     */
 
153
    pMI->txt_zero_fd = -1;
 
154
    pMI->txt_errno   = 0;
 
155
 
 
156
    {
 
157
        void* pNuls;
 
158
#ifdef _SC_PAGESIZE
 
159
        size_t pgsz = sysconf(_SC_PAGESIZE);
 
160
#else
 
161
        size_t pgsz = getpagesize();
 
162
#endif
 
163
        /*
 
164
         *  Compute the pagesize rounded mapped memory size.
 
165
         *  IF this is not the same as the file size, then there are NUL's
 
166
         *  at the end of the file mapping and all is okay.
 
167
         */
 
168
        pMI->txt_full_size = (pMI->txt_size + (pgsz - 1)) & ~(pgsz - 1);
 
169
        if (pMI->txt_size != pMI->txt_full_size)
 
170
            return pMI->txt_data;
 
171
 
 
172
        /*
 
173
         *  Still here?  We have to remap the trailing inaccessible page
 
174
         *  either anonymously or to /dev/zero.
 
175
         */
 
176
        pMI->txt_full_size += pgsz;
 
177
#if defined(MAP_ANONYMOUS)
 
178
        pNuls = mmap(
 
179
                (void*)(((char*)pMI->txt_data) + pMI->txt_size),
 
180
                pgsz, PROT_READ|PROT_WRITE,
 
181
                MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE, AO_INVALID_FD, 0 );
 
182
 
 
183
        if (pNuls != MAP_FAILED_PTR)
 
184
            return pMI->txt_data;
 
185
 
 
186
        pMI->txt_errno = errno;
 
187
 
 
188
#elif defined(HAVE_DEV_ZERO)
 
189
        pMI->txt_zero_fd = open( "/dev/zero", O_RDONLY );
 
190
 
 
191
        if (pMI->txt_zero_fd == AO_INVALID_FD) {
 
192
            pMI->txt_errno = errno;
 
193
 
 
194
        } else {
 
195
            pNuls = mmap(
 
196
                    (void*)(((char*)pMI->txt_data) + pMI->txt_size), pgsz,
 
197
                    PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED,
 
198
                    pMI->txt_zero_fd, 0 );
 
199
 
 
200
            if (pNuls != MAP_FAILED_PTR)
 
201
                return pMI->txt_data;
 
202
 
 
203
            pMI->txt_errno = errno;
 
204
            close( pMI->txt_zero_fd );
 
205
            pMI->txt_zero_fd = -1;
 
206
        }
 
207
#endif
 
208
 
 
209
        pMI->txt_full_size = pMI->txt_size;
 
210
    }
 
211
 
 
212
    {
 
213
        void* p = AGALOC( pMI->txt_size+1, "file text" );
 
214
        memcpy( p, pMI->txt_data, pMI->txt_size );
 
215
        ((char*)p)[pMI->txt_size] = NUL;
 
216
        munmap(pMI->txt_data, pMI->txt_size );
 
217
        pMI->txt_data = p;
 
218
    }
 
219
    pMI->txt_alloc = 1;
 
220
    return pMI->txt_data;
 
221
 
 
222
#else /* * * * * * no HAVE_MMAP * * * * * */
 
223
 
 
224
    pMI->txt_data = AGALOC( pMI->txt_size+1, "file text" );
 
225
    if (pMI->txt_data == NULL) {
 
226
        pMI->txt_errno = ENOMEM;
 
227
        goto fail_return;
 
228
    }
 
229
 
 
230
    {
 
231
        size_t sz = pMI->txt_size;
 
232
        char*  pz = pMI->txt_data;
 
233
 
 
234
        while (sz > 0) {
 
235
            ssize_t rdct = read( pMI->txt_fd, pz, sz );
 
236
            if (rdct <= 0) {
 
237
                pMI->txt_errno = errno;
 
238
                fprintf( stderr, zFSErrReadFile,
 
239
                         errno, strerror( errno ), pzFile );
 
240
                free( pMI->txt_data );
 
241
                goto fail_return;
 
242
            }
 
243
 
 
244
            pz += rdct;
 
245
            sz -= rdct;
 
246
        }
 
247
 
 
248
        *pz = NUL;
 
249
    }
 
250
 
 
251
    /*
 
252
     *  We never need a dummy page mapped in
 
253
     */
 
254
    pMI->txt_zero_fd = -1;
 
255
    pMI->txt_errno   = 0;
 
256
 
 
257
    return pMI->txt_data;
 
258
 
 
259
#endif /* * * * * * no HAVE_MMAP * * * * * */
 
260
 
 
261
 fail_return:
 
262
    if (pMI->txt_fd >= 0) {
 
263
        close( pMI->txt_fd );
 
264
        pMI->txt_fd = -1;
 
265
    }
 
266
    errno = pMI->txt_errno;
 
267
    pMI->txt_data = MAP_FAILED_PTR;
 
268
    return pMI->txt_data;
 
269
}
 
270
 
 
271
 
 
272
/*=export_func  text_munmap
 
273
 * private:
 
274
 *
 
275
 * what:  unmap the data mapped in by text_mmap
 
276
 *
 
277
 * arg:   tmap_info_t*, mapinfo, info about the mapping
 
278
 *
 
279
 * ret-type:   int
 
280
 * ret-desc:   -1 or 0.  @file{errno} will have the error code.
 
281
 *
 
282
 * doc:
 
283
 *
 
284
 * This routine will unmap the data mapped in with @code{text_mmap} and close
 
285
 * the associated file descriptors opened by that function.
 
286
 *
 
287
 * see: munmap(2), close(2)
 
288
 *
 
289
 * err: Any error code issued by munmap(2) or close(2) is possible.
 
290
=*/
 
291
int
 
292
text_munmap( tmap_info_t* pMI )
 
293
{
 
294
#ifdef HAVE_MMAP
 
295
    int res = 0;
 
296
    if (pMI->txt_alloc) {
 
297
        /*
 
298
         *  IF the user has write permission and the text is not mapped private,
 
299
         *  then write back any changes.  Hopefully, nobody else has modified
 
300
         *  the file in the mean time.
 
301
         */
 
302
        if (   ((pMI->txt_prot & PROT_WRITE) != 0)
 
303
            && ((pMI->txt_flags & MAP_PRIVATE) == 0))  {
 
304
 
 
305
            if (lseek( pMI->txt_fd, 0, SEEK_SET) != 0)
 
306
                goto error_return;
 
307
 
 
308
            res = (write( pMI->txt_fd, pMI->txt_data, pMI->txt_size ) < 0)
 
309
                ? errno : 0;
 
310
        }
 
311
 
 
312
        AGFREE( pMI->txt_data );
 
313
        errno = res;
 
314
    } else {
 
315
        res = munmap( pMI->txt_data, pMI->txt_full_size );
 
316
    }
 
317
    if (res != 0)
 
318
        goto error_return;
 
319
 
 
320
    res = close( pMI->txt_fd );
 
321
    if (res != 0)
 
322
        goto error_return;
 
323
 
 
324
    pMI->txt_fd = -1;
 
325
    errno = 0;
 
326
    if (pMI->txt_zero_fd != -1) {
 
327
        res = close( pMI->txt_zero_fd );
 
328
        pMI->txt_zero_fd = -1;
 
329
    }
 
330
 
 
331
 error_return:
 
332
    pMI->txt_errno = errno;
 
333
    return res;
 
334
#else  /* HAVE_MMAP */
 
335
 
 
336
    errno = 0;
 
337
    /*
 
338
     *  IF the memory is writable *AND* it is not private (copy-on-write)
 
339
     *     *AND* the memory is "sharable" (seen by other processes)
 
340
     *  THEN rewrite the data.
 
341
     */
 
342
    if (   FILE_WRITABLE(pMI->txt_prot, pMI->txt_flags)
 
343
        && (lseek( pMI->txt_fd, 0, SEEK_SET ) >= 0) ) {
 
344
        write( pMI->txt_fd, pMI->txt_data, pMI->txt_size );
 
345
    }
 
346
 
 
347
    close( pMI->txt_fd );
 
348
    pMI->txt_fd = -1;
 
349
    pMI->txt_errno = errno;
 
350
    free( pMI->txt_data );
 
351
 
 
352
    return pMI->txt_errno;
 
353
#endif /* HAVE_MMAP */
 
354
}
 
355
 
 
356
/*
 
357
 * Local Variables:
 
358
 * mode: C
 
359
 * c-file-style: "stroustrup"
 
360
 * indent-tabs-mode: nil
 
361
 * End:
 
362
 * end of autoopts/text_mmap.c */