On 18/04/2025 06:18, Jim Meyering wrote:
On Tue, Apr 15, 2025 at 9:17 PM Jim Meyering <j...@meyering.net> 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



Reply via email to