[Toybox] I aten't dead.

Rob Landley rob at landley.net
Tue Feb 24 21:50:16 PST 2015



On 02/24/2015 03:59 PM, Rich Felker wrote:
> On Tue, Feb 24, 2015 at 01:50:03PM -0800, enh wrote:
>> On Tue, Feb 24, 2015 at 1:48 PM, Rich Felker <dalias at libc.org> wrote:
>>> On Tue, Feb 24, 2015 at 01:30:20PM -0800, enh wrote:
>>>> On Tue, Feb 24, 2015 at 11:46 AM, Rob Landley <rob at landley.net> wrote:
>>>>> Sorry for the radio silence, $DAYJOB's been eating the majority of my
>>>>> programming time and what's left has gone to Aboriginal Linux recently,
>>>>> because I made a largeish design change over there (putting the base
>>>>> root filesystem in initmpfs and merging the native-compiler at runtime
>>>>> instead of compile time), and the aboriginal linux release soft-blocks
>>>>> the toybox release because I use "built aboriginal and then built linux
>>>>> from scratch under it" as my main toybox regression-smoketest.
>>>>>
>>>>> Last night's I hit a fun bug in toybox "stat" where it doesn't remotely
>>>>> work on arm. (Works fine on x86 host, but 3.18 kernel built against
>>>>> uClibc on armv5l: the -f numbers are way off, it thinks the blocksize of
>>>>> ext2fs is 32 bytes.)
>>>>
>>>> seems fine on armv7 3.10 with bionic. oh, specifically the -f output.
>>>> yeah, that looks wrong :-)
>>>>
>>>> a lot of those fields are actually 64-bit, so you need an extra 'l' on LP32:
>>>>
>>>> diff --git a/toys/other/stat.c b/toys/other/stat.c
>>>> index d603316..a96c1de 100644
>>>> --- a/toys/other/stat.c
>>>> +++ b/toys/other/stat.c
>>>> @@ -106,11 +106,11 @@ static void print_stat(char type)
>>>>  static void print_statfs(char type) {
>>>>    struct statfs *statfs = (struct statfs *)&TT.stat;
>>>>
>>>> -  if (type == 'a') xprintf("%lu", statfs->f_bavail);
>>>> -  else if (type == 'b') xprintf("%lu", statfs->f_blocks);
>>>> -  else if (type == 'c') xprintf("%lu", statfs->f_files);
>>>> -  else if (type == 'd') xprintf("%lu", statfs->f_ffree);
>>>> -  else if (type == 'f') xprintf("%lu", statfs->f_bfree);
>>>> +  if (type == 'a') xprintf("%llu", statfs->f_bavail);
>>>> +  else if (type == 'b') xprintf("%llu", statfs->f_blocks);
>>>> +  else if (type == 'c') xprintf("%llu", statfs->f_files);
>>>> +  else if (type == 'd') xprintf("%llu", statfs->f_ffree);
>>>> +  else if (type == 'f') xprintf("%llu", statfs->f_bfree);
>>>>    else if (type == 'l') xprintf("%ld", statfs->f_namelen);
>>>>    else if (type == 't') xprintf("%lx", statfs->f_type);
>>>>    else if (type == 'i')

Yeah, that looks like the fix.

>>> Rather than hard-coding an assumption that these have type unsigned
>>> long long, the code should be casting them to unsigned long long or
>>> uintmax_t and using an appropriate format specifier. I prefer
>>> uintmax_t since it's idiomatic and can be used for formatting any
>>> unsigned type, but I know Rob can be allergic to these nice semantic
>>> types... :-)

I've used uint64_t sometimes, but they're really silly names and LP64 is
a real thing. The only part that's _not_ quite standardized is that
"long long" is "at least" 64 bits, meaning it could be 128. But it
currently isn't anywhere that I can find, and the uint64_t
implementations all boil down to unsigned long long, so...

No, what annoys me here is that the types in the man page are ridiculous:

   struct statfs {
       __SWORD_TYPE f_type;    /* type of filesystem (see below) */
       __SWORD_TYPE f_bsize;   /* optimal transfer block size */
       fsblkcnt_t   f_blocks;  /* total data blocks in filesystem */
       fsblkcnt_t   f_bfree;   /* free blocks in fs */
       fsblkcnt_t   f_bavail;  /* free blocks available to
                                  unprivileged user */
       fsfilcnt_t   f_files;   /* total file nodes in filesystem */
       fsfilcnt_t   f_ffree;   /* free file nodes in fs */
       fsid_t       f_fsid;    /* filesystem id */
       __SWORD_TYPE f_namelen; /* maximum length of filenames */
       __SWORD_TYPE f_frsize;  /* fragment size (since Linux 2.6) */
       __SWORD_TYPE f_spare[5];
   };

(In this cae "SWORD_TYPE" is "katana".) The reason buried later in the
man page is that statfs got replaced with statfs64 and they tried to
hide the differences with magic macros.

What it _also_ says is that there's a posix statvfs in which f_fsid is
unsigned long, instead of unsigned long long. So I could switch from the
LSB to the POSIX thing, at the expense of having a data type mangled.

>> yeah, i almost said the same but because i know that you can't even
>> rely on the signedness being the same between different architectures
>> i didn't want to get into that argument :-)
> 
> I wouldn't worry about the signedness unless negative values are
> actually meaningful. Here they're not so an unsigned type is safe; the
> full positive range of any type fits in uintmax_t.

On 32 bit systems glibc does horrible things under the covers. I don't
hugely care, but gratuitously unnecessary conversions are not a
substitute for understanding what your code is actually doing.

> Rich

Rob

 1424843416.0


More information about the Toybox mailing list