Exemplo n.º 1
0
// To reduce the number of string copies, this function calling pattern works as follows:
// 1) The managed code calls GetDirentSize() to get the platform-specific
//    size of the dirent struct.
// 2) The managed code creates a byte[] buffer of the size of the native dirent
//    and passes a pointer to this buffer to this function.
// 3) This function passes input byte[] buffer to the OS to fill with dirent
//    data which makes the 1st strcpy.
// 4) The ConvertDirent function will fill DirectoryEntry outputEntry with
//    pointers from byte[] buffer.
// 5) The managed code uses DirectoryEntry outputEntry to find start of d_name
//    and the value of d_namelen, if avalable, to copy the name from
//    byte[] buffer into a managed string that the caller can use; this makes
//    the 2nd and final strcpy.
extern "C" int32_t SystemNative_ReadDirR(DIR* dir, void* buffer, int32_t bufferSize, DirectoryEntry* outputEntry)
{
    assert(buffer != nullptr);
    assert(dir != nullptr);
    assert(outputEntry != nullptr);

    if (bufferSize < static_cast<int32_t>(sizeof(dirent)))
    {
        assert(false && "Buffer size too small; use GetDirentSize to get required buffer size");
        return ERANGE;
    }

    dirent* result = nullptr;
    dirent* entry = static_cast<dirent*>(buffer);
#if HAVE_READDIR_R
    int error = readdir_r(dir, entry, &result);

    // positive error number returned -> failure
    if (error != 0)
    {
        assert(error > 0);
        *outputEntry = {}; // managed out param must be initialized
        return error;
    }

    // 0 returned with null result -> end-of-stream
    if (result == nullptr)
    {
        *outputEntry = {}; // managed out param must be initialized
        return -1;         // shim convention for end-of-stream
    }

    // 0 returned with non-null result (guaranteed to be set to entry arg) -> success
    assert(result == entry);
#else
    errno = 0;
    result = readdir(dir);

    // 0 returned with null result -> end-of-stream
    if (result == nullptr)
    {
        *outputEntry = {}; // managed out param must be initialized

        //  kernel set errno -> failure
        if (errno != 0)
        {
            assert(errno == EBADF); // Invalid directory stream descriptor dir.
            return errno;
        }
        return -1;
    }

    assert(result->d_reclen <= bufferSize);
    memcpy(entry, result, static_cast<size_t>(result->d_reclen));
#endif
    ConvertDirent(*entry, outputEntry);
    return 0;
}
Exemplo n.º 2
0
int32_t ReadDirR(DIR* dir, void* buffer, int32_t bufferSize, DirectoryEntry* outputEntry)
{
    assert(buffer != nullptr);
    assert(dir != nullptr);
    assert(outputEntry != nullptr);

    if (bufferSize < sizeof(dirent))
    {
        assert(!"Buffer size too small; use GetDirentSize to get required buffer size");
        return ERANGE;
    }

    // On successful cal to readdir_r, result and &entry should point to the same
    // data; a NULL temp pointer but return of 0 means that we reached the end of the 
    // directory stream; finally, a NULL temp pointer with a positive return value
    // means an error occurred.
    dirent* result = nullptr;
    dirent* entry = (dirent*)buffer;
    int ret = readdir_r(dir, entry, &result);
    if (ret == 0)
    {
        if (result != nullptr)
        {
            assert(result == entry);
            ConvertDirent(*entry, outputEntry);
        }
        else
        {
            ret = -1; // errno values are positive so signal the end-of-stream with a non-error value
            *outputEntry = { };
        }
    }
    else
    {
        *outputEntry = { };
    }

    return ret;
}
Exemplo n.º 3
0
// To reduce the number of string copies, this function calling pattern works as follows:
// 1) The managed code calls GetDirentSize() to get the platform-specific
//    size of the dirent struct.
// 2) The managed code creates a byte[] buffer of the size of the native dirent
//    and passes a pointer to this buffer to this function.
// 3) This function passes input byte[] buffer to the OS to fill with dirent data
//    which makes the 1st strcpy.
// 4) The ConvertDirent function will set a pointer to the start of the inode name
//    in the byte[] buffer so the managed code and find it and copy it out of the
//    buffer into a managed string that the caller of the framework can use, making
//    the 2nd and final strcpy.
extern "C" int32_t ReadDirR(DIR* dir, void* buffer, int32_t bufferSize, DirectoryEntry* outputEntry)
{
    assert(buffer != nullptr);
    assert(dir != nullptr);
    assert(outputEntry != nullptr);

    if (bufferSize < static_cast<int32_t>(sizeof(dirent)))
    {
        assert(false && "Buffer size too small; use GetDirentSize to get required buffer size");
        return ERANGE;
    }

    dirent* result = nullptr;
    dirent* entry = static_cast<dirent*>(buffer);
    int error = readdir_r(dir, entry, &result);

    // positive error number returned -> failure
    if (error != 0)
    {
        assert(error > 0);
        *outputEntry = {}; // managed out param must be initialized
        return error;
    }

    // 0 returned with null result -> end-of-stream
    if (result == nullptr)
    {
        *outputEntry = {}; // managed out param must be initialized
        return -1;         // shim convention for end-of-stream
    }

    // 0 returned with non-null result (guaranteed to be set to entry arg) -> success
    assert(result == entry);
    ConvertDirent(*entry, outputEntry);
    return 0;
}
Exemplo n.º 4
0
// To reduce the number of string copies, the caller of this function is responsible to ensure the memory
// referenced by outputEntry remains valid until it is read.
// If the platform supports readdir_r, the caller provides a buffer into which the data is read.
// If the platform uses readdir, the caller must ensure no calls are made to readdir/closedir since those will invalidate
// the current dirent. We assume the platform supports concurrent readdir calls to different DIRs.
int32_t SystemNative_ReadDirR(DIR* dir, uint8_t* buffer, int32_t bufferSize, DirectoryEntry* outputEntry)
{
    assert(dir != NULL);
    assert(outputEntry != NULL);

#if HAVE_READDIR_R
    assert(buffer != NULL);

    // align to dirent
    struct dirent* entry = (struct dirent*)((size_t)(buffer + dirent_alignment - 1) & ~(dirent_alignment - 1));

    // check there is dirent size available at entry
    if ((buffer + bufferSize) < ((uint8_t*)entry + sizeof(struct dirent)))
    {
        assert(false && "Buffer size too small; use GetReadDirRBufferSize to get required buffer size");
        return ERANGE;
    }

    struct dirent* result = NULL;
#ifdef _AIX
    // AIX returns 0 on success, but bizarrely, it returns 9 for both error and
    // end-of-directory. result is NULL for both cases. The API returns the
    // same thing for EOD/error, so disambiguation between the two is nearly
    // impossible without clobbering errno for yourself and seeing if the API
    // changed it. See:
    // https://www.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.basetrf2/readdir_r.htm

    errno = 0; // create a success condition for the API to clobber
    int error = readdir_r(dir, entry, &result);

    if (error == 9)
    {
        memset(outputEntry, 0, sizeof(*outputEntry)); // managed out param must be initialized
        return errno == 0 ? -1 : errno;
    }
#else
    int error = readdir_r(dir, entry, &result);

    // positive error number returned -> failure
    if (error != 0)
    {
        assert(error > 0);
        memset(outputEntry, 0, sizeof(*outputEntry)); // managed out param must be initialized
        return error;
    }

    // 0 returned with null result -> end-of-stream
    if (result == NULL)
    {
        memset(outputEntry, 0, sizeof(*outputEntry)); // managed out param must be initialized
        return -1;         // shim convention for end-of-stream
    }
#endif

    // 0 returned with non-null result (guaranteed to be set to entry arg) -> success
    assert(result == entry);
#else
    (void)buffer;     // unused
    (void)bufferSize; // unused
    errno = 0;
    struct dirent* entry = readdir(dir);

    // 0 returned with null result -> end-of-stream
    if (entry == NULL)
    {
        memset(outputEntry, 0, sizeof(*outputEntry)); // managed out param must be initialized

        //  kernel set errno -> failure
        if (errno != 0)
        {
            assert_err(errno == EBADF, "Invalid directory stream descriptor dir", errno);
            return errno;
        }
        return -1;
    }
#endif
    ConvertDirent(entry, outputEntry);
    return 0;
}