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