[Toybox] ls -l broken

enh enh at google.com
Wed Nov 20 22:15:09 PST 2019


this was my original version, btw, which was a bit clearer. (i thought
you'd object to the repetition of `okay =`.)

it makes it more obvious that failing the first fstatat is not okay,
but if we both try and succeed the second fstatat, that makes up for
it:

    // stat dangling symlinks
    if (fstatat(fd, name, &st, sym)) {
      okay = 0;
      if (errno == ENOENT && !sym &&
              !fstatat(fd, name, &st, AT_SYMLINK_NOFOLLOW)) okay = 1;
    }

and then the statless++/goto error below happens whichever path you
take. (because remember that in the ls(1) case, errno == ENOENT and
sym != 0, so you can't succeed the second if in the current code.)

    if (!okay) {
      if (flags&DIRTREE_STATLESS) statless++;
      else goto error;
    }

On Wed, Nov 20, 2019 at 9:53 PM enh <enh at google.com> wrote:
>
> On Wed, Nov 20, 2019 at 9:52 PM enh <enh at google.com> wrote:
> >
> > On Wed, Nov 20, 2019 at 9:44 PM Rob Landley <rob at landley.net> wrote:
> > >
> > > On 11/20/19 4:28 PM, enh via Toybox wrote:
> > > > [PATCH] dirtree: fix 0b2cfcb8fdea9673f3c2e0940f1b16d5825e16ea.
> > > >
> > > > 0b2cfcb8fdea9673f3c2e0940f1b16d5825e16ea broke `ls -l` on files that
> > > > don't exist.
> > > >
> > > > We didn't spot this because the corresponding ls test (which did exist)
> > > > had accidentally been mangled in a couple of ways.
> > >
> > > Sigh. THIS is why I hate testing for specific error messages. (I'm guessing
> > > different' libc versions are giving different error messages, so this will break
> > > again next year.)
> > >
> > > > This patch fixes the test (and extends it to differentiate between a
> > > > couple of possible ways it can go wrong), and fixes the broken path in
> > > > dirtree_add_node.
> > >
> > > I'm flying again today so got up early and brain's screwed up because of it, but
> > > I don't understand why you moved this test:
> > >
> > > +      sym = AT_SYMLINK_NOFOLLOW*!(flags&DIRTREE_SYMFOLLOW), okay = 1;
> > >
> > >      // stat dangling symlinks
> > >      if (fstatat(fd, name, &st, sym)) {
> > > -      if (errno != ENOENT
> > > -        || (!sym && fstatat(fd, name, &st, AT_SYMLINK_NOFOLLOW)))
> > > -      {
> > > -        if (flags&DIRTREE_STATLESS) statless++;
> > > -        else goto error;
> > > -      }
> > > +      okay = (errno == ENOENT && !sym &&
> > > +              !fstatat(fd, name, &st, AT_SYMLINK_NOFOLLOW));
> > > +    }
> > > +    if (!okay) {
> > > +      if (flags&DIRTREE_STATLESS) statless++;
> > > +      else goto error;
> > >      }
> > >
> > > If "okay" can only be zeroed when we go into the first fstatat()'s if() block,
> > > what does moving the test to after the block accomplish? You don't use "okay"
> > > again outside that stanza...
> >
> > the point is that before the ls case where the first fstatat fails
> > couldn't fall through to the statless++/goto error block. so that
> > needed to move out, and then you need a way to tell how you got there.
> > (otherwise you _either_ fail the find tests _or_ you fail the [fixed]
> > ls tests.)
> >
> > > Let's see, old test:
> > >
> > >   if (errno != ENOENT || (!sym && fstatat()))
> > >
> > > was effectively replaced with:
> > >
> > >   if (!(errno == ENOENT && !sym && !fstatat()))
> > >
> > > But a straight inversion of the first test would be...
> > >
> > >   if (!(errno == ENOENT && (sym || !fstatat())))
> > >
> > > So it _looks_ like the fix you applied is basically just changing the existing
> > > test to:
> > >
> > >   if (errno != ENOENT || sym || fstatat())
> >
> > no, the important part is moving the statless++/goto error part out
> > far enough that you can get to it whether or not you do a second
> > fstatat.
> >
> > the currently checked in code just carries on with an uninitialized struct stat.
> >
> > > The intent of "statless" being set was to return "exists but could not stat".
> > > So we set it when the first stat fails, but the error isn't ENOENT and we
> > > couldn't symYour change will do that when fstatat() fails with an error other
> > > than ENOENT, if readdir returns something we can't stat without following symlinks.
> > >
> > > Sigh. Why is ls defaulting to showing nanoseconds? How does this help? It makes
> > > the output way wider and provides no useful information in most cases. (Debian's
> > > ls -l doesn't even default to showing _seconds_...)
> > >
> > > I'm too sleep deprived to think through this right now. (And I broke the
> > > individual command builds on monday. Great.)
>
> (and i'm not sure what you mean by this. `export VERBOSE=1 ; make
> test_find && make test_ls` was working fine for me today. though
> you'll need to take my fix to the ls test to get any benefit from the
> latter half.)
>
> > > Rob



More information about the Toybox mailing list