/*++ 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 #include #include #if !defined(__amd64__) && !defined(__aarch64__) #include #endif #include #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, duping STDOUT should 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; }