WSL/test/linux/unit_tests/dup.c
WSL Team 697572d664 Initial open source commit for WSL.
Many Microsoft employees have contributed to the Windows Subsystem for Linux, this commit is the result of their work since 2016.

The entire history of the Windows Subsystem for Linux can't be shared here, but here's an overview of WSL's history after it moved to it own repository in 2021:

Number of commits on the main branch: 2930
Number of contributors: 31

Head over https://github.com/microsoft/WSL/releases for a more detailed history of the features added to WSL since 2021.
2025-05-15 12:09:45 -07:00

296 lines
No EOL
6.5 KiB
C

/*++
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;
}