nixpkgs/pkgs/development/libraries/glibc/nix-locale-archive.patch
George Shammas de64f49396 glibc: fix locale fallback on non-nixos systems
There is a nixpkgs' glibc patch that makes it look at environment variables to
locate the locale-archive. It has a fallback to /usr/lib/locale/locale-archive,
which is a pretty good one since nearly all non-nixos systems use that exact
path since glibc hard codes it looking at PREFIX/lib/locale/locale-archive. IE
debian/EL/Arch/Gentoo and others all use the same path for locale-archive. That
hard coding in glibc is why nixpkgs even has this patch in the first place.
Other wise you would need to rebuild every single package whenever you wanted to
change the locale-archive at runtime.

The problem with that final fallback is that glibc in nixpkgs includes a minimal
locale-archive with just C.UTF-8. It does this so tests that require UTF-8
support can work in nixpkgs. The current patch will _always_ find the minimal
locale-archive and never actually fall back to the system one.

This can be very confusing, especially when looking at the patch and wondering
why your CI system is throwing locale errors. Eventually you realize that some
test sanitizes the environment, but still confused since it should fall back to
the system's locale-archive.

On systems that do not set LOCALE_ARCHIVE and do not have
/usr/lib/locale/locale-archive then we fall back to that super minimal
locale-archive included with the default nixpkgs' glibc.

Testing:
== Before ==
% env -u LOCALE_ARCHIVE_2_27 -u LOCALE_ARCHIVE LANG=en_US.UTF-8 /nix/store/gdq185xa4cmka46nv67vkmjlmkd9yr1k-cowsay-3.8.3/bin/cowsay hello
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
        LANGUAGE = "",
        LC_ALL = (unset),
        LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
 _______
< hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

== After ==
% env -u LOCALE_ARCHIVE_2_27 -u LOCALE_ARCHIVE LANG=en_US.UTF-8 /nix/store/hhc2gzs6gax4frk8985w2ada2ra35zyw-cowsay-3.8.3/bin/cowsay hello
 _______
< hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

=== On NixOS ===
% env -u LOCALE_ARCHIVE_2_27 -u LOCALE_ARCHIVE LANG=en_US.UTF-8 /nix/store/hhc2gzs6gax4frk8985w2ada2ra35zyw-cowsay-3.8.3/bin/cowsay hello
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
        LANGUAGE = (unset),
        LC_ALL = (unset),
        LC_CTYPE = (unset),
        LC_NUMERIC = (unset),
        LC_COLLATE = (unset),
        LC_TIME = (unset),
        LC_MESSAGES = (unset),
        LC_MONETARY = (unset),
        LC_ADDRESS = (unset),
        LC_IDENTIFICATION = (unset),
        LC_MEASUREMENT = (unset),
        LC_PAPER = (unset),
        LC_TELEPHONE = (unset),
        LC_NAME = (unset),
        LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
 _______
< hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

=== Still can get the minimal locale-archive included with glibc ===
% env -u LOCALE_ARCHIVE_2_27 -u LOCALE_ARCHIVE LANG=C.UTF-8 /nix/store/hhc2gzs6gax4frk8985w2ada2ra35zyw-cowsay-3.8.3/bin/cowsay hello
 _______
< hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
2024-12-02 19:17:31 -05:00

122 lines
3.7 KiB
Diff

diff --git a/locale/loadarchive.c b/locale/loadarchive.c
index 512769eaec..171dbb4ad9 100644
--- a/locale/loadarchive.c
+++ b/locale/loadarchive.c
@@ -123,6 +123,23 @@ calculate_head_size (const struct locarhead *h)
return MAX (namehash_end, MAX (string_end, locrectab_end));
}
+static int
+open_locale_archive (void)
+{
+ int fd = -1;
+ char *versioned_path = getenv ("LOCALE_ARCHIVE_2_27");
+ char *path = getenv ("LOCALE_ARCHIVE");
+ if (versioned_path)
+ fd = __open_nocancel (versioned_path, O_RDONLY|O_LARGEFILE|O_CLOEXEC);
+ if (path && fd < 0)
+ fd = __open_nocancel (path, O_RDONLY|O_LARGEFILE|O_CLOEXEC);
+ if (fd < 0)
+ fd = __open_nocancel ("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE|O_CLOEXEC);
+ if (fd < 0)
+ fd = __open_nocancel (archfname, O_RDONLY|O_LARGEFILE|O_CLOEXEC);
+ return fd;
+}
+
/* Find the locale *NAMEP in the locale archive, and return the
internalized data structure for its CATEGORY data. If this locale has
@@ -202,7 +219,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
archmapped = &headmap;
/* The archive has never been opened. */
- fd = __open_nocancel (archfname, O_RDONLY|O_LARGEFILE|O_CLOEXEC);
+ fd = open_locale_archive ();
if (fd < 0)
/* Cannot open the archive, for whatever reason. */
return NULL;
@@ -397,8 +414,7 @@ _nl_load_locale_from_archive (int category, const char **namep)
if (fd == -1)
{
struct __stat64_t64 st;
- fd = __open_nocancel (archfname,
- O_RDONLY|O_LARGEFILE|O_CLOEXEC);
+ fd = open_locale_archive();
if (fd == -1)
/* Cannot open the archive, for whatever reason. */
return NULL;
diff --git a/locale/programs/locale.c b/locale/programs/locale.c
index ca0a95be99..e484783402 100644
--- a/locale/programs/locale.c
+++ b/locale/programs/locale.c
@@ -633,6 +633,24 @@ nameentcmp (const void *a, const void *b)
}
+static int
+open_locale_archive (void)
+{
+ int fd = -1;
+ char *versioned_path = getenv ("LOCALE_ARCHIVE_2_27");
+ char *path = getenv ("LOCALE_ARCHIVE");
+ if (versioned_path)
+ fd = open64 (versioned_path, O_RDONLY);
+ if (path && fd < 0)
+ fd = open64 (path, O_RDONLY);
+ if (fd < 0)
+ fd = open64 ("/usr/lib/locale/locale-archive", O_RDONLY);
+ if (fd < 0)
+ fd = open64 (ARCHIVE_NAME, O_RDONLY);
+ return fd;
+}
+
+
static int
write_archive_locales (void **all_datap, char *linebuf)
{
@@ -645,7 +663,7 @@ write_archive_locales (void **all_datap, char *linebuf)
int fd, ret = 0;
uint32_t cnt;
- fd = open64 (ARCHIVE_NAME, O_RDONLY);
+ fd = open_locale_archive ();
if (fd < 0)
return 0;
diff --git a/locale/programs/locarchive.c b/locale/programs/locarchive.c
index f38e835c52..779a3199fc 100644
--- a/locale/programs/locarchive.c
+++ b/locale/programs/locarchive.c
@@ -117,6 +117,22 @@ prepare_address_space (int fd, size_t total, size_t *reserved, int *xflags,
}
+static int
+open_locale_archive (const char * archivefname, int flags)
+{
+ int fd = -1;
+ char *versioned_path = getenv ("LOCALE_ARCHIVE_2_27");
+ char *path = getenv ("LOCALE_ARCHIVE");
+ if (versioned_path)
+ fd = open64 (versioned_path, flags);
+ if (path && fd < 0)
+ fd = open64 (path, flags);
+ if (fd < 0)
+ fd = open64 (archivefname, flags);
+ return fd;
+}
+
+
static void
create_archive (const char *archivefname, struct locarhandle *ah)
{
@@ -578,7 +594,7 @@ open_archive (struct locarhandle *ah, bool readonly)
while (1)
{
/* Open the archive. We must have exclusive write access. */
- fd = open64 (archivefname, readonly ? O_RDONLY : O_RDWR);
+ fd = open_locale_archive (archivefname, readonly ? O_RDONLY : O_RDWR);
if (fd == -1)
{
/* Maybe the file does not yet exist? If we are opening