]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commit
fuse2fs: fix logging redirection
authorDarrick J. Wong <djwong@kernel.org>
Tue, 22 Jul 2025 19:40:53 +0000 (12:40 -0700)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 31 Jul 2025 14:46:02 +0000 (10:46 -0400)
commitf79abd8554e600eacc2a7c864a8332b670c9e262
tree65a5bf90c97b70bbd6b0f22fc94791e9579229a8
parente18b350af2d77f1e063ad9ae765dd161022bb04a
fuse2fs: fix logging redirection

Someone pointed out that you can't just go around reassigning stdout and
stderr because section 7.23.1 paragraph 4 of a recent C2y draft says
that stdin, stdout, and stderr “are expressions of type "pointer to
FILE" that point to the FILE objects associated, respectively, with the
standard error, input, and output streams.”

The use of the word "expression" should have been the warning sign that
a symbol that can be mostly used as a pointer is not simply a pointer.

Seven pages later, footnote 318 of the C2y draft clarifies that stdin,
stdout, and stderr “need not be modifiable lvalues to which the value
returned by the fopen function could be assigned.”

"need not be" is the magic phrasing that means that glibc, musl, and
mingw (for example) have very different declarations of stdout:

glibc:
extern FILE *stdout;           /* Standard output stream.  */

musl:
extern FILE *const stdout;

mingw:
#define stdout (&_iob[STDOUT_FILENO])

All three are following the specification, yet you can write C code that
fails to compile what otherwise looks like a normal assignment on two of
the libraries:

static FILE *const fark; /* musl */

FILE crap[3]; /* mingw */
#define crows (&crap[0])

static FILE *stupid; /* glibc */

int main(int argc, char *argv[])
{
fark = NULL;
crows = NULL;
stupid = NULL;
}

/tmp/a.c: In function ‘main’:
/tmp/a.c:20:14: error: assignment of read-only variable ‘fark’
   20 |         fark = NULL;
      |              ^
/tmp/a.c:21:15: error: lvalue required as left operand of assignment
   21 |         crows = NULL;
      |               ^

What a useless specification!  You don't even get the same error!
Unfortunately, this leadership vacuum means that each implementation of
a so-called standard C library is perfectly within its right to do this.
IOWs, the authors decided that every C programmer must divert some of
the brainpower they could spend on their program's core algorithms to be
really smart about this quirk.

A whole committee of very smart programmers collectively decided this
was a good way to run things decades ago so that C library authors in
the 1980s wouldn't have to change their code, and subsequent gatherings
have reaffirmed this "practical" decision.  Their suggestion to reassign
stdout and stderr is to use freopen, but that walks the specified path,
which is racy if you want both streams to point to the same file.  You
could pass /dev/fd/XX to solve the race, but then you lose portability.

In other words, they "engineered" an incomplete solution with problems
to achieve a short term goal that nobody should care about 40 years
later.

Fix fuse2fs by rearranging the code to change STD{OUT,ERR}_FILENO to
point to the same open logfile via dup2 and then try to use freopen on
/dev/fd/XX to capture any stdout/err usage by the libraries that fuse2fs
depends on.

Note that we must do the whole thing over again in op_init because
libfuse will dup2 STD{OUT,ERR}_FILE to /dev/null as part of daemonizing
the server.

Cc: linux-ext4@vger.kernel.org # v1.47.3
Fixes: 5cdebf3eebc22c ("fuse2fs: stop aliasing stderr with ff->err_fp")
Link: https://github.com/tytso/e2fsprogs/issues/235
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Link: https://lore.kernel.org/r/20250722194053.GO2672022@frogsfrogsfrogs
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
misc/fuse2fs.c