[PATCH] libstdc++: Use realpath for /etc/localtime symlink [PR125467]
Jonathan Wakely
jwakely.gcc@gmail.com
Mon Jun 29 10:38:42 GMT 2026
On Mon, 29 Jun 2026 at 11:31, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
>
> On Mon, 29 Jun 2026 at 10:21, Tomasz Kaminski <tkaminsk@redhat.com> wrote:
> >
> >
> >
> > On Mon, Jun 29, 2026 at 10:34 AM Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
> >>
> >>
> >>
> >> On Mon, 29 Jun 2026, 07:23 Tomasz Kaminski, <tkaminsk@redhat.com> wrote:
> >>>
> >>>
> >>>
> >>> On Fri, Jun 26, 2026 at 7:02 PM Jonathan Wakely <jwakely@redhat.com> wrote:
> >>>>
> >>>> Although the systemd docs say that /etc/localtime should be a symlink to
> >>>> one of the zoneinfo files, some systems make it a symlink to another
> >>>> path, where that second path is a symlink to a zoneinfo file (e.g. if
> >>>> /etc is mounted read-only then /etc/localtime can be a symlink to
> >>>> another symlink on a writable disk, so that the system timezone can be
> >>>> altered by re-pointing the symlink on the writable disk).
> >>>>
> >>>> In that case, using readlink would only tell us the location of the
> >>>> second symlink, not which zoneinfo file it points to. Therefore, we
> >>>> would not be able to extract a valid time zone name from the path, and
> >>>> chrono::current_zone() would fail.
> >>>>
> >>>> To support multiple symlinks we could recursively keep resolving
> >>>> symlinks with readlink until we reach a path from which we can extract a
> >>>> zone name. Alternatively, we can just use realpath to resolve all
> >>>> symlinks to a physical file, which will be . This means we only need one system call and
> >>>> don't need the extra complexity of calling readlink in a loop.
> >>>>
> >>>> The realpath system call also removes redunant slashes, so we can remove
> >>>> the code that did that manually.
> >>>>
> >>>> The possible downsides of this approach that I'm aware of are:
> >>>>
> >>>> - When /etc/localtime is a symlink to /invalid/Europe/London but that
> >>>> file doesn't exist. With the previous implementation we would have
> >>>> resolved that symlink to the zone "Europe/London" as long as that name
> >>>> is known to the current chrono::tzdb object. With this change, we
> >>>> won't get a valid zone name and current_zone() will fail. I'm not sure
> >>>> how realistic this case is. It might be plausible if libstdc++ is
> >>>> using the embedded static copy of tzdata.zi and there are no zoneinfo
> >>>> files on disk at all. In that case the system might still use
> >>>> /etc/localtime to name a zone, even though the symlink is dangling.
> >>>> Maybe we could fall back to filesystem::weakly_canonical for this
> >>>> case?
> >>>
> >>> I would be concerned about doing that silently, as this could lead to
> >>> non-local errors in the deployed environment are extremely hard to debug.
> >>> What I mean is the situation where tzdata should be mounted.
> >>> If the mounting fails, we will resolve the zone without issue,
> >>
> >>
> >> Will we? We might resolve a name, but it still needs to be looked up in tzdb to find a time_zone. If there's no tzdata that will fail (unless it's one of the zones that is always present like UTC and GMT).
> >
> > From my reading, we fail first to the embeded static tzdb data if opening tzdb file fails:
> > tzdata_stream() : istream(nullptr)
> > {
> > if (string path = zoneinfo_file(tzdata_file); !path.empty())
> > {
> > filebuf fbuf;
> > if (fbuf.open(path, std::ios::in))
> > {
> > std::construct_at(&fb, std::move(fbuf));
> > this->init(&fb);
> > return;
> > }
> > }
> > std::construct_at(&sb);
> > this->init(&sb);
> > }
> > We can successfully look up the zone if we know about it, even if we cannot read the zone database.
>
> Right - but that seems orthogonal to chrono::current_zone(), and I
> don't think it is an argument against using weakly_canonical to deal
> with dangling /etc/localtime symlinks.
>
> The semantics of current_zone are to decide on a name, then use
> locate_zone(str) to look that name up (and maybe retry if the lookup
> fails and we can try a different name). The presence or absence of a
> valid tzdb object affects the locate_zone(str) step but I don't think
> it should affect the process for deciding on a name. With the code on
> trunk today, we can still extract a name from a dangling symlink (but
> we can't extract a name from a chain of two or more symlinks). With my
> proposed patch we would be able to extract a name from a chain of
> symlinks, but would not be able to get anything from dangling
> symlinks.
>
> > Maybe I am missing something here.
> >
> >>
> >>
> >>> and then only
> >>> If the data requires updating, the update will fail. From that perspective I much
> >>> more prefer loud failure.
> >>
> >>
> >> A dangling symlink to the Etc/GMT or UTC zone would always work OK even if there is no tzdata, and maybe that's the expected behaviour for some system.
> >
> > This system would be better served by having TZDB disabled, if that is the consistent behavior.
>
> That requires a custom build of libstdc++.so whereas a minimal
> container could use the system libstdc++.so and remove all the tzdata
> files to save disk space.
Which is a supported use case in Fedora (since F39, and so also true
for RHEL 10):
https://fedoraproject.org/wiki/Changes/AllowRemovalOfTzdata
But libstdc++ doesn't need to cope with dangling symlinks to make the
"Etc/UTC" case work because if we don't find anything for
/etc/localtime then we default to UTC anyway:
https://gcc.gnu.org/cgit/gcc/commit/?id=4abd5bc600193e821fbc41995a0b8d9ea42b42c3
More information about the Libstdc++
mailing list