diff options
-rw-r--r-- | locale/loadarchive.c | 78 |
1 files changed, 42 insertions, 36 deletions
diff --git a/locale/loadarchive.c b/locale/loadarchive.c index 59ac916e29..845b9b0774 100644 --- a/locale/loadarchive.c +++ b/locale/loadarchive.c @@ -41,6 +41,10 @@ /* Name of the locale archive file. */ static const char archfname[] = LOCALEDIR "/locale-archive"; +/* Size of initial mapping window, optimal if large enough to + cover the header plus the initial locale. */ +#define ARCHIVE_MAPPING_WINDOW (2 * 1024 * 1024) + /* Record of contiguous pages already mapped from the locale archive. */ struct archmapped @@ -174,6 +178,9 @@ _nl_load_locale_from_archive (int category, const char **namep) /* Make sure the archive is loaded. */ if (archmapped == NULL) { + void *result; + size_t headsize, mapsize; + /* We do this early as a sign that we have tried to open the archive. If headmap.ptr remains null, that's an indication that we tried and failed, so we won't try again. */ @@ -193,49 +200,48 @@ _nl_load_locale_from_archive (int category, const char **namep) return NULL; } - if (sizeof (void *) > 4) - { - /* We will just map the whole file, what the hell. */ - void *result = __mmap64 (NULL, archive_stat.st_size, - PROT_READ, MAP_SHARED, fd, 0); - if (result == MAP_FAILED) - goto close_and_out; - /* Check whether the file is large enough for the sizes given in the - header. */ - if (calculate_head_size ((const struct locarhead *) result) - > archive_stat.st_size) - { - (void) __munmap (result, archive_stat.st_size); - goto close_and_out; - } - __close (fd); - fd = -1; - headmap.ptr = result; - /* headmap.from already initialized to zero. */ - headmap.len = archive_stat.st_size; - } - else - { - struct locarhead head; - off_t head_size; - void *result; + /* Map an initial window probably large enough to cover the header + and the first locale's data. With a large address space, we can + just map the whole file and be sure everything is covered. */ - if (TEMP_FAILURE_RETRY (__read (fd, &head, sizeof (head))) - != sizeof (head)) - goto close_and_out; - head_size = calculate_head_size (&head); - if (head_size > archive_stat.st_size) + mapsize = (sizeof (void *) > 4 ? archive_stat.st_size + : MAX (archive_stat.st_size, ARCHIVE_MAPPING_WINDOW)); + + result = __mmap64 (NULL, mapsize, PROT_READ, MAP_SHARED, fd, 0); + if (result == MAP_FAILED) + goto close_and_out; + + /* Check whether the file is large enough for the sizes given in + the header. Theoretically an archive could be so large that + just the header fails to fit in our initial mapping window. */ + headsize = calculate_head_size ((const struct locarhead *) result); + if (headsize > mapsize) + { + (void) __munmap (result, mapsize); + if (sizeof (void *) > 4 || headsize > archive_stat.st_size) + /* The file is not big enough for the header. Bogus. */ goto close_and_out; - result = __mmap64 (NULL, head_size, PROT_READ, MAP_SHARED, fd, 0); + + /* Freakishly long header. */ + /* XXX could use mremap when available */ + mapsize = (headsize + ps - 1) & ~(ps - 1); + result = __mmap64 (NULL, mapsize, PROT_READ, MAP_SHARED, fd, 0); if (result == MAP_FAILED) goto close_and_out; + } - /* Record that we have mapped the initial pages of the file. */ - headmap.ptr = result; - headmap.len = MIN ((head_size + ps - 1) & ~(ps - 1), - archive_stat.st_size); + if (sizeof (void *) > 4 || mapsize >= archive_stat.st_size) + { + /* We've mapped the whole file already, so we can be + sure we won't need this file descriptor later. */ + __close (fd); + fd = -1; } + + headmap.ptr = result; + /* headmap.from already initialized to zero. */ + headmap.len = mapsize; } /* If there is no archive or it cannot be loaded for some reason fail. */ |