diff options
author | Tony Cook <tony@develop-help.com> | 2021-07-09 10:22:40 +1000 |
---|---|---|
committer | Tony Cook <tony@develop-help.com> | 2021-09-01 10:59:44 +1000 |
commit | 9b5699737a6b587546239d586832f420cf7f2dea (patch) | |
tree | 37f4fec318866b00f20095ba66f60faa44788cd6 /pp_sys.c | |
parent | b852e1da77b497e086508451bebff00541073fb1 (diff) | |
download | perl-9b5699737a6b587546239d586832f420cf7f2dea.tar.gz |
detect struct stat.st_dev's size and signedness, and return it safely
On FreeBSD dev_t (and hence the st_dev member of struct stat) is an
unsigned 64-bit integer, and the previous simple PUSHi() corrupted
that.
A previous version of this reflected the st_ino code and implemented
our own number to string conversion, but a system with such a large
st_dev should be assumed to have inttypes.h, and an intmax_t which is
no smaller than st_dev.
The st_ino code could probably be changed similarly, but 64-bit inode
numbers are not a new thing, so it may be riskier.
Diffstat (limited to 'pp_sys.c')
-rw-r--r-- | pp_sys.c | 31 |
1 files changed, 31 insertions, 0 deletions
@@ -2916,7 +2916,38 @@ PP(pp_stat) if (max) { EXTEND(SP, max); EXTEND_MORTAL(max); +#if ST_DEV_SIZE < IVSIZE || (ST_DEV_SIZE == IVSIZE && ST_DEV_SIGN < 0) mPUSHi(PL_statcache.st_dev); +#elif ST_DEV_SIZE == IVSIZE + mPUSHu(PL_statcache.st_dev); +#else +# if ST_DEV_SIGN < 0 + if (LIKELY((IV)PL_statcache.st_dev == PL_statcache.st_dev)) { + mPUSHi((IV)PL_statcache.st_dev); + } +# else + if (LIKELY((UV)PL_statcache.st_dev == PL_statcache.st_dev)) { + mPUSHu((UV)PL_statcache.st_dev); + } +# endif + else { + char buf[sizeof(PL_statcache.st_dev)*3+1]; + /* sv_catpvf() casts 'j' size values down to IV, so it + isn't suitable for use here. + */ +# if defined(I_INTTYPES) && defined(HAS_SNPRINTF) +# if ST_DEV_SIGN < 0 + int size = snprintf(buf, sizeof(buf), "%" PRIdMAX, (intmax_t)PL_statcache.st_dev); +# else + int size = snprintf(buf, sizeof(buf), "%" PRIuMAX, (uintmax_t)PL_statcache.st_dev); +# endif + STATIC_ASSERT_STMT(sizeof(intmax_t) >= sizeof(PL_statcache.st_dev)); + mPUSHp(buf, size); +# else +# error extraordinarily large st_dev but no inttypes.h or no snprintf +# endif + } +#endif { /* * We try to represent st_ino as a native IV or UV where |