WSL/test/linux/unit_tests/dup.c

296 lines
6.5 KiB
C
Raw Normal View History

/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
dup.c
Abstract:
This file is a test for the dup, dup2 system call.
--*/
#include "lxtcommon.h"
#include "unittests.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#if !defined(__amd64__) && !defined(__aarch64__)
#include <sys/limits.h>
#endif
#include <fcntl.h>
#define LXT_NAME "Dup"
#define FD_INVALID (-1)
#define FD_STDIN 0
#define FD_STDOUT 1
#define FD_ERR 2
#define MY_F_DUPFD_CLOEXEC 1030
int Dup0(PLXT_ARGS Args);
int Dup1(PLXT_ARGS Args);
int Dup2(PLXT_ARGS Args);
//
// Global constants
//
static const LXT_VARIATION g_LxtVariations[] = {{"Dup Basic", Dup0}, {"Dup Descriptor Flags", Dup1}, {"FCNTL Dup Error Cases", Dup2}};
int DupTestEntry(int Argc, char* Argv[])
/*++
Routine Description:
This routine main entry point for the test for dup, dup2 system call.
Arguments:
Argc - Supplies the number of command line arguments.
Argv - Supplies the command line arguments.
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
LXT_ARGS Args;
int Result;
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
ErrorExit:
LxtUninitialize();
return !LXT_SUCCESS(Result);
}
int Dup0(PLXT_ARGS Args)
/*++
Routine Description:
This routine validates that the dup and dup2 system call and the
various parameter variances.
Arguments:
Args - Supplies the command line arguments.
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
int Result;
int DupStdIn;
int DupStdOut;
int DupFd2;
LxtCheckErrnoFailure(dup(FD_INVALID), EBADF);
LxtCheckErrnoFailure(dup(__SHRT_MAX__), EBADF);
LxtCheckResult((DupStdIn = dup(FD_STDIN)));
LxtCheckResult(close(FD_STDIN));
LxtCheckResult((DupStdOut = dup(FD_STDOUT)));
//
// Since we just closed STDIN, dup'ing STDOUT should take take the position
// of STDIN
//
if (DupStdOut != FD_STDIN)
{
LxtLogError("Dup, expected FD return value(%d), actual(%d).", FD_STDIN, DupStdOut);
Result = LXT_RESULT_FAILURE;
goto ErrorExit;
}
//
// Now forcefully restore STDIN to its rightful position using dup2
//
LxtCheckResult((DupFd2 = dup2(DupStdIn, FD_STDIN)));
if (DupFd2 != FD_STDIN)
{
LxtLogError("Dup, expected FD return value(%d), actual(%d).", FD_STDIN, DupFd2);
Result = LXT_RESULT_FAILURE;
goto ErrorExit;
}
Result = LXT_RESULT_SUCCESS;
ErrorExit:
return Result;
}
int Dup1(PLXT_ARGS Args)
/*++
Routine Description:
This routine validates that the variations of the dup system calls,
and their behavior w.r.t sharing file descriptor flags.
Arguments:
Args - Supplies the command line arguments.
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
int Result;
int FdProcSelf;
int FdProcSelfFlags;
int FdDup;
int FdDupFlags;
int FdTemp;
LxtCheckResult((FdProcSelf = open("/proc/self", O_RDONLY | O_CLOEXEC)));
LxtCheckResult((FdDup = dup(FdProcSelf)));
LxtCheckResult((FdProcSelfFlags = fcntl(FdProcSelf, F_GETFD)));
LxtCheckResult((FdDupFlags = fcntl(FdDup, F_GETFD)));
if ((FdProcSelfFlags & FD_CLOEXEC) == 0)
{
LxtLogError("/proc/self FD should have the 'FD_CLOEXEC' flag set, ", "but it is not. FD Flags(%d).", FdProcSelfFlags);
Result = LXT_RESULT_FAILURE;
goto ErrorExit;
}
//
// Dup should not share file descriptor flags
//
if ((FdDupFlags & FD_CLOEXEC) != 0)
{
LxtLogError(
"Dup should not share File Descriptor Flags between "
"the old and new descriptor. New Descriptor Flags(%d).",
FdDupFlags);
Result = LXT_RESULT_FAILURE;
goto ErrorExit;
}
LxtCheckResult(close(FdDup));
LxtCheckResult((FdTemp = fcntl(FdProcSelf, F_DUPFD, FdDup)));
if (FdTemp != FdDup)
{
LxtLogError(
"fcntl(F_DUPFD) should return(%d), but "
"it returned fd(%d).",
FdDup,
FdTemp);
Result = LXT_RESULT_FAILURE;
goto ErrorExit;
}
LxtCheckResult((FdDupFlags = fcntl(FdDup, F_GETFD)));
//
// fcntl(F_DUPFD) should not set the FD_CLOEXEC in the new descriptor
//
if ((FdDupFlags & FD_CLOEXEC) != 0)
{
LxtLogError(
"fcntl(F_DUPFD) should not share File Descriptor Flags "
"between the old and new descriptor. New Descriptor "
"Flags(%d).",
FdDupFlags);
Result = LXT_RESULT_FAILURE;
goto ErrorExit;
}
LxtCheckResult(close(FdDup));
LxtCheckResult((FdTemp = fcntl(FdProcSelf, MY_F_DUPFD_CLOEXEC, FdDup)));
if (FdTemp != FdDup)
{
LxtLogError(
"fcntl(F_DUPFD_CLOEXEC) should return(%d), but "
"it returned fd(%d).",
FdDup,
FdTemp);
Result = LXT_RESULT_FAILURE;
goto ErrorExit;
}
LxtCheckResult((FdDupFlags = fcntl(FdDup, F_GETFD)));
//
// fcntl(F_DUPFD_CLOEXEC) should set the FD_CLOEXEC in the new descriptor
//
if ((FdDupFlags & FD_CLOEXEC) == 0)
{
LxtLogError(
"fcntl(F_DUPFD_CLOEXEC) should set the FD_CLOEXEC flag "
"in the new descriptor. New Descriptor "
"Flags(%d).",
FdDupFlags);
Result = LXT_RESULT_FAILURE;
goto ErrorExit;
}
Result = LXT_RESULT_SUCCESS;
ErrorExit:
return Result;
}
int Dup2(PLXT_ARGS Args)
/*++
Routine Description:
This routine validates the error cases for FCNTL calls related to dup.
Arguments:
Args - Supplies the command line arguments.
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
int Result;
LxtCheckErrnoFailure(fcntl(FD_INVALID, F_DUPFD, 0), EBADF);
LxtCheckErrnoFailure(fcntl(FD_INVALID, MY_F_DUPFD_CLOEXEC, 0), EBADF);
LxtCheckErrnoFailure(fcntl(FD_STDIN, F_DUPFD, FD_INVALID), EINVAL);
LxtCheckErrnoFailure(fcntl(FD_STDIN, MY_F_DUPFD_CLOEXEC, FD_INVALID), EINVAL);
//
// TODO: Add cases where the FD to dup into in FCNTL is > max allowed
//
Result = LXT_RESULT_SUCCESS;
ErrorExit:
return Result;
}