Do not impose an arbitrary file name length limit on platforms
like GNU/Hurd that do not define PATH_MAX.
Conversely, when PATH_MAX is defined (e.g., GNU/Linux, the BSDs),
speed things up a bit if the TZ string is very long.
* NEWS: Mention this.
* localtime.c (union local_storage.fullname):
Now of size PATH_MAX if PATH_MAX is defined, and removed otherwise.
All uses changed.
(tzloadbody) [!PATH_MAX]: If the TZ string is too long,
allocate a larger buffer instead of failing with ENAMETOOLONG.
This happens only with artificial TZ strings like
"America/./././Los_Angeles" except with many more "/."s.
This supports the GNU/Hurd philosophy of no arbitrary limits.
(tzloadbody) [PATH_MAX]: Speed things up a bit.
(tzload): Call ‘free’ even if !ALL_STATE, if tzloadbody allocated
a larger buffer.  With decent optimization this call is optimized
away if PATH_MAX && !ALL_STATE (the default case on most platforms).
---
 NEWS        |  4 ++++
 localtime.c | 35 ++++++++++++++++++++++-------------
 2 files changed, 26 insertions(+), 13 deletions(-)

diff --git a/NEWS b/NEWS
index 5b00817f..64d9336d 100644
--- a/NEWS
+++ b/NEWS
@@ -77,6 +77,10 @@ Unreleased, experimental changes
     tzset etc. now have an experimental OPENAT_TZDIR option;
     see Makefile and localtime.c for details.
 
+    On platforms like GNU/Hurd that do not define PATH_MAX,
+    exceedingly long TZ strings no longer fail merely because they
+    exceed an arbitrary file name length limit imposed by tzcode.
+
   Changes to commentary
 
     The leapseconds file contains commentary about the IERS and NIST
diff --git a/localtime.c b/localtime.c
index c7b9f663..7ab8aa41 100644
--- a/localtime.c
+++ b/localtime.c
@@ -689,13 +689,10 @@ union local_storage {
     struct state st;
   } u;
 
-  /* The name of the file to be opened.  Ideally this would have no
-     size limits, to support arbitrarily long Zone names.
-     Limiting Zone names to 1024 bytes should suffice for practical use.
-     However, there is no need for this to be smaller than struct
-     file_analysis as that struct is allocated anyway, as the other
-     union member.  */
-  char fullname[max(sizeof(struct file_analysis), sizeof TZDIR + 1024)];
+#ifdef PATH_MAX
+  /* The name of the file to be opened.  */
+  char fullname[PATH_MAX];
+#endif
 };
 
 /* These tzload flags can be ORed together, and fit into 'char'.  */
@@ -788,25 +785,35 @@ tzloadbody(char const *name, struct state *sp, char 
tzloadflags,
 
        if (!OPENAT_TZDIR && !SUPPRESS_TZDIR && name[0] != '/') {
                char *cp;
-               size_t namesizemax = sizeof lsp->fullname - tzdirslashlen;
+               size_t fullnamesize;
+#ifdef PATH_MAX
+               static_assert(tzdirslashlen <= PATH_MAX);
+               size_t namesizemax = PATH_MAX - tzdirslashlen;
                size_t namelen = strnlen (name, namesizemax);
                if (namesizemax <= namelen)
                  return ENAMETOOLONG;
+#else
+               size_t namelen = strlen (name);
+#endif
+               fullnamesize = tzdirslashlen + namelen + 1;
 
                /* Create a string "TZDIR/NAME".  Using sprintf here
                   would pull in stdio (and would fail if the
                   resulting string length exceeded INT_MAX!).  */
-               if (ALL_STATE) {
-                 lsp = malloc(sizeof *lsp);
+               if (ALL_STATE || sizeof *lsp <= fullnamesize) {
+                 lsp = malloc(max(sizeof *lsp, fullnamesize));
                  if (!lsp)
                    return HAVE_MALLOC_ERRNO ? errno : ENOMEM;
                  *lspp = lsp;
                }
-               cp = lsp->fullname;
-               cp = mempcpy(cp, tzdirslash, tzdirslashlen);
+               cp = mempcpy(lsp, tzdirslash, tzdirslashlen);
                cp = mempcpy(cp, name, namelen);
                *cp = '\0';
+#ifdef PATH_MAX
                name = lsp->fullname;
+#else
+               name = (char *) lsp;
+#endif
        }
 
        fid = OPENAT_TZDIR ? openat(dd, relname, oflags) : open(name, oflags);
@@ -1086,6 +1093,7 @@ static int
 tzload(char const *name, struct state *sp, char tzloadflags)
 {
   int r;
+  union local_storage *lsp0;
   union local_storage *lsp;
 #if ALL_STATE
   lsp = NULL;
@@ -1093,8 +1101,9 @@ tzload(char const *name, struct state *sp, char 
tzloadflags)
   union local_storage ls;
   lsp = &ls;
 #endif
+  lsp0 = lsp;
   r = tzloadbody(name, sp, tzloadflags, &lsp);
-  if (ALL_STATE)
+  if (lsp != lsp0)
     free(lsp);
   return r;
 }
-- 
2.51.0

Reply via email to