mirror of
https://github.com/HowardHinnant/date.git
synced 2024-12-26 16:01:04 +08:00
Stop using PATH_MAX
PATH_MAX isn't guaranteed to be defined in Posix environments; it is only on systems that have a path length limit, and even in environments where it is defined its usage can lead to issues. To avoid using PATH_MAX, I've made two main changes: - Where realpath() was used, I've changed the code to use its [POSIX.1-2008]'s new behaviour, where passing a null pointer as the resolved_name buffer results in realpath() to automatically allocate a buffer large enough to handle the given path, that is returned to the caller. This has been supported for a long time as a GNU libc extension before being standardized. - Where readlink() was used, the size of the buffer was already determined when calling lstat(); the returned struct stat contains a st_size field, containing the number of bytes needed to store the symbolic link contents. This meant that to avoid using the tricky define I only needed to use a dynamically allocated buffer instead of a static one, of size stat.st_size (+1 when a null terminator is needed). To make sure that memory is always freed, I've wrapped the new dynamic allocations in an std::unique_ptr. The pointer returned by realpath() must be freed with free(), so a unique_ptr with a custom deleter that calls free() on destruction was used. To read more about why PATH_MAX leads to buggy code I'd suggest reading something like this: <https://eklitzke.org/path-max-is-tricky>. [POSIX.1-2008]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
This commit is contained in:
parent
ab1dc3a5eb
commit
ac0c58d5da
34
src/tz.cpp
34
src/tz.cpp
@ -488,9 +488,10 @@ discover_tz_dir()
|
|||||||
if (!(lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0))
|
if (!(lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0))
|
||||||
throw runtime_error("discover_tz_dir failed\n");
|
throw runtime_error("discover_tz_dir failed\n");
|
||||||
string result;
|
string result;
|
||||||
char rp[PATH_MAX+1] = {};
|
unique_ptr<char[]> rp(new char[sb.st_size]);
|
||||||
if (readlink(timezone, rp, sizeof(rp)-1) > 0)
|
const auto rp_length = readlink(timezone, rp.get(), sb.st_size);
|
||||||
result = string(rp);
|
if (rp_length > 0)
|
||||||
|
result = string(rp.get(), rp_length); // readlink doesn't null-terminate
|
||||||
else
|
else
|
||||||
throw system_error(errno, system_category(), "readlink() failed");
|
throw system_error(errno, system_category(), "readlink() failed");
|
||||||
auto i = result.find("zoneinfo");
|
auto i = result.find("zoneinfo");
|
||||||
@ -4026,10 +4027,10 @@ bool
|
|||||||
sniff_realpath(const char* timezone)
|
sniff_realpath(const char* timezone)
|
||||||
{
|
{
|
||||||
using namespace std;
|
using namespace std;
|
||||||
char rp[PATH_MAX+1] = {};
|
unique_ptr<char, decltype(free) *> rp(realpath(timezone, nullptr), free);
|
||||||
if (realpath(timezone, rp) == nullptr)
|
if (rp.get() == nullptr)
|
||||||
throw system_error(errno, system_category(), "realpath() failed");
|
throw system_error(errno, system_category(), "realpath() failed");
|
||||||
auto result = extract_tz_name(rp);
|
auto result = extract_tz_name(rp.get());
|
||||||
return result != "posixrules";
|
return result != "posixrules";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4056,18 +4057,24 @@ tzdb::current_zone() const
|
|||||||
{
|
{
|
||||||
using namespace std;
|
using namespace std;
|
||||||
static const bool use_realpath = sniff_realpath(timezone);
|
static const bool use_realpath = sniff_realpath(timezone);
|
||||||
char rp[PATH_MAX+1] = {};
|
|
||||||
if (use_realpath)
|
if (use_realpath)
|
||||||
{
|
{
|
||||||
if (realpath(timezone, rp) == nullptr)
|
unique_ptr<char, decltype(free) *> rp(realpath(timezone, nullptr), free);
|
||||||
|
if (rp.get() == nullptr)
|
||||||
throw system_error(errno, system_category(), "realpath() failed");
|
throw system_error(errno, system_category(), "realpath() failed");
|
||||||
|
return locate_zone(extract_tz_name(rp.get()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (readlink(timezone, rp, sizeof(rp)-1) <= 0)
|
// +1 because st_size doesn't include the '\0' terminator
|
||||||
|
const auto rp_size = sb.st_size + 1;
|
||||||
|
unique_ptr<char[]> rp(new char[rp_size]);
|
||||||
|
const auto rp_length = readlink(timezone, rp.get(), rp_size);
|
||||||
|
if (rp_length <= 0)
|
||||||
throw system_error(errno, system_category(), "readlink() failed");
|
throw system_error(errno, system_category(), "readlink() failed");
|
||||||
|
rp.get()[rp_length] = '\0'; // readlink doesn't null-terminate
|
||||||
|
return locate_zone(extract_tz_name(rp.get()));
|
||||||
}
|
}
|
||||||
return locate_zone(extract_tz_name(rp));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// On embedded systems e.g. buildroot with uclibc the timezone is linked
|
// On embedded systems e.g. buildroot with uclibc the timezone is linked
|
||||||
@ -4086,9 +4093,10 @@ tzdb::current_zone() const
|
|||||||
if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) {
|
if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
string result;
|
string result;
|
||||||
char rp[PATH_MAX+1] = {};
|
unique_ptr<char[]> rp(new char[sb.st_size]);
|
||||||
if (readlink(timezone, rp, sizeof(rp)-1) > 0)
|
const auto rp_length = readlink(timezone, rp.get(), sb.st_size);
|
||||||
result = string(rp);
|
if (rp_length > 0)
|
||||||
|
result = string(rp.get(), rp_length); // readlink doesn't null-terminate
|
||||||
else
|
else
|
||||||
throw system_error(errno, system_category(), "readlink() failed");
|
throw system_error(errno, system_category(), "readlink() failed");
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user