Subject: bug#77800: grep-3.12: write-error-msg test failure on fedora rawhide (f43)

From: Pádraig Brady <[email-masked]>
Date: Fri, 18 Apr 2025 11:40:17 +0100

On 18/04/2025 06:18, Jim Meyering wrote:
> On Tue, Apr 15, 2025 at 9:17 PM Jim Meyering <[email-masked]> wrote:
> ...
>> We're going to have to revise that code.
>> The difference I see is that before rawhide, that fclose would fail.
>> It's perfectly fine for fclose to succeed in this case, as now happens
>> on rawhide (because with 4k BUFSIZ, the fclose wrote nothing -- the
>> preceding 4096-byte write is what failed).
>>
>> Here's a better patch: (technically, we could factor it somewhat, but
>> readability would suffer disproportionately)
> 
> I didn't take the time to find a precise commit, but this bug predates
> the move from closeout.c to gnulib's close-stdout.c in 2006. As I
> write this, I'm installing Fedora 42.
> I'll probably push the attached to gnulib tomorrow:

The variance here seems to be due to stdio buffer size:

  $ for b in 0 4096 8192; do
      echo bs=$b
      for ws in 4095 4096 4097; do
        printf "ws=$ws: "
        stdbuf -o${b} printf %${ws}s >/dev/full
      done
    done

  bs=0
  ws=4095: printf: write error
  ws=4096: printf: write error
  ws=4097: printf: write error
  bs=4096
  ws=4095: printf: write error: No space left on device
  ws=4096: printf: write error
  ws=4097: printf: write error
  bs=8192
  ws=4095: printf: write error: No space left on device
  ws=4096: printf: write error: No space left on device
  ws=4097: printf: write error: No space left on device

I.e. write() always gives ENOSPC, it just whether it's called or not.
I.e. we get the more exact error if it's latent until we fflush() at exit.

$ for i in 4095 4096 4097; do ltrace -e fwrite -e fclose -e fflush -e ferror printf %${i}s >/dev/full; done
printf->fwrite("                                "..., 1, 4095, 0x7f15ca8225c0)                = 4095
printf->fflush(0x7f15ca8225c0)                                                                = -1
printf->fclose(0x7f15ca8225c0)                                                                = 0
printf: write error: No space left on device
+++ exited (status 1) +++
printf->fwrite("                                "..., 1, 4096, 0x7fb38377e5c0)                = 0
printf->ferror(0x7fb38377e5c0)                                                                = 1
printf->fflush(0x7fb38377e5c0)                                                                = 0
printf->fclose(0x7fb38377e5c0)                                                                = 0
printf: write error
+++ exited (status 1) +++
printf->fwrite("                                "..., 1, 4097, 0x7fd0372695c0)                = 0
printf->ferror(0x7fd0372695c0)                                                                = 1
printf->fflush(0x7fd0372695c0)                                                                = 0
printf->fclose(0x7fd0372695c0)                                                                = 0
printf: write error
+++ exited (status 1) +++

If a utility wants to give a more exact error it could operate like certain coreutils commands
and issue a specific error at fwrite (or other stdio function) call time.
Note one has to be careful to not output multiple errors in that case,
so it's not always appropriate to follow that course, but if appropriate
(to exit immediately) then ref the coreutils write_error() function:
https://github.com/coreutils/coreutils/blob/0d04b985/src/system.h#L750-L757

Now printf (vasnprintf) doesn't diagnose such write errors immediately,
so --help output may not be the route to ensure an ENOSPC error.
A hacky solution might be to change the test to use --version rather than --help,
to output a smaller amount of data, but that assumes the output is buffered.
It might be appropriate instead to just not look for the specific ENOSPC error.

cheers,
Pádraig