JUCE assumes fd can never be 0 (bug?)

In FileOutputStream/FileInputStream and other places it casts a file handle to a void*. It then assumes that this cannot be valid and nullptr, i.e. the original handle from open() cannot be 0. This is not true, 0 is a valid fd.

We have added a hack/workaround to our JUCE copy where we were seeing the problem:

if (fd == 0)
{
  reopen
  fd = newOne
  close(oldOne)
}

Maybe a similar hack should be adopted? I’m sure a more fully formed fix could also be constructed.

Nasty c to show it is possible:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    printf("start num fds\n");
    system("ls /proc/self/fd");

    printf("open fd\n");
    int fd1 = open("/dev/null", O_RDONLY, 00644);
    printf("new fd: %d\n", fd1);

    printf("num fds after open\n");
    system("ls /proc/self/fd");

    close(0);

    printf("num fds after close(0)\n");
    system("ls /proc/self/fd");

    printf("open fd2\n");
    int fd2 = open("/dev/null", O_RDONLY, 00644);
    printf("new fd: %d\n", fd2);

    printf("num fds after open fd2\n");
    system("ls /proc/self/fd");
}

Correct me if I’m wrong here - but I thought this was platform specific behavior where 0 is either invalid or reserved?

@Holy_City I am not sure when 0 is allowed and when it is not, but it is not always disallowed which is the problem here I believe.

While from a C perspective a NULL FILE pointer is technically valid, on most platforms that value is reserved by the system. Like on Unix you can’t read from /dev/null, you can only write to it.

What I’m saying is that it makes sense for JUCE not to treat a file pointer of 0 as a valid file, since it’s not. It’s a special behavior used by some systems, so if you need to use it you should be writing platform specific code.

Here is some JUCE code that reproduces the issue. “After fclose(stdin)” will not be in the output file on Mac or Linux (don’t know about Windows or other).

/*******************************************************************************
 The block below describes the properties of this PIP. A PIP is a short snippet
 of code that can be read by the Projucer and used to generate a JUCE project.

 BEGIN_JUCE_PIP_METADATA

  name:             FDBug
  version:          0.1

  dependencies:     juce_core
  exporters:        xcode_mac

  moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1

  type:             Console

 END_JUCE_PIP_METADATA

*******************************************************************************/

#pragma once


//==============================================================================
int main (int argc, char* argv[])
{
    File temp(File::getSpecialLocation(File::userHomeDirectory).getChildFile("FDBug"));
    temp.deleteFile();
    DBG("Temp file name: " + temp.getFullPathName().quoted());

    {
        ScopedPointer<FileOutputStream> fo = temp.createOutputStream();
        if (!fo->writeString("Before fclose(stdin)\n"))
        {
            DBG("Couldn't write to temp file");
            return 1;
        }
    }

    fclose(stdin);

    {
        ScopedPointer<FileOutputStream> fo = temp.createOutputStream();
        if (!fo->writeString("After fclose(stdin)\n"))
        {
            DBG("Couldn't write to temp file");
            return 1;
        }
    }

    return 0;
}

The checks for if (fileHandle == nullptr) are in juce_posix_SharedCode.h, so that already is platform specific code (just for many platforms).

bump?