xref: /reactos/sdk/lib/ucrt/lowio/dup2.cpp (revision b09b5584)
1 //
2 // dup2.cpp
3 //
4 //      Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines _dup2() and _dup2_nolock, which duplicate lowio file handles
7 //
8 #include <corecrt_internal_lowio.h>
9 #include <corecrt_internal_ptd_propagation.h>
10 
11 static int __cdecl _dup2_nolock_internal(int const source_fh, int const target_fh, __crt_cached_ptd_host& ptd) throw()
12 {
13     if ((_osfile(source_fh) & FOPEN) == 0)
14     {
15         // If the source handle is not open, return an error.  Noe that the
16         // DuplicateHandle API will not detect this error, because it implies
17         // that _osfhnd(source_fh) == INVALID_HANDLE_VALUE, and this is a
18         // legal HANDLE value to be duplicated.
19         ptd.get_errno().set(EBADF);
20         ptd.get_doserrno().set(0);
21         _ASSERTE(("Invalid file descriptor. File possibly closed by a different thread",0));
22         return -1;
23     }
24 
25     // Duplicate the source file onto the target file:
26     intptr_t new_osfhandle;
27 
28     BOOL const result = DuplicateHandle(
29         GetCurrentProcess(),
30         reinterpret_cast<HANDLE>(_get_osfhandle(source_fh)),
31         GetCurrentProcess(),
32         &reinterpret_cast<HANDLE&>(new_osfhandle),
33         0,
34         TRUE,
35         DUPLICATE_SAME_ACCESS);
36 
37     if (!result)
38     {
39         __acrt_errno_map_os_error_ptd(GetLastError(), ptd);
40         return -1;
41     }
42 
43     // If the target is open, close it once we know the OS handle was duplicated successfully.
44     // We ignore the possibility of an error here: an error simply means that the OS handle
45     // value may remain bound for the duration of the process.
46     if (_osfile(target_fh) & FOPEN)
47     {
48         _close_nolock_internal(target_fh, ptd);
49     }
50 
51     __acrt_lowio_set_os_handle(target_fh, new_osfhandle);
52 
53     // Copy the _osfile information, with the FNOINHERIT bit cleared:
54     _osfile(target_fh) = _osfile(source_fh) & ~FNOINHERIT;
55     _textmode(target_fh) = _textmode(source_fh);
56     _tm_unicode(target_fh) = _tm_unicode(source_fh);
57 
58     return 0;
59 }
60 
61 static int __cdecl _dup2_internal(int const source_fh, int const target_fh, __crt_cached_ptd_host& ptd) throw()
62 {
63     _UCRT_CHECK_FH_CLEAR_OSSERR_RETURN(ptd, source_fh, EBADF, -1);
64     _UCRT_VALIDATE_CLEAR_OSSERR_RETURN(ptd, (source_fh >= 0 && (unsigned)source_fh < (unsigned)_nhandle), EBADF, -1);
65     _UCRT_VALIDATE_CLEAR_OSSERR_RETURN(ptd, (_osfile(source_fh) & FOPEN), EBADF, -1);
66     _UCRT_CHECK_FH_CLEAR_OSSERR_RETURN(ptd, target_fh, EBADF, -1);
67     _UCRT_VALIDATE_CLEAR_OSSERR_RETURN(ptd, ((unsigned)target_fh < _NHANDLE_), EBADF, -1);
68 
69     // Make sure there is an __crt_lowio_handle_data struct corresponding to the target_fh:
70     if (target_fh >= _nhandle && __acrt_lowio_ensure_fh_exists(target_fh) != 0)
71     {
72         return -1;
73     }
74 
75     // If the source and target are the same, return success (we've already
76     // verified that the file handle is open, above).  This is for conformance
77     // with the POSIX specification for dup2.
78     if (source_fh == target_fh)
79     {
80         return 0;
81     }
82 
83     // Obtain the two file handle locks.  In order to prevent deadlock, we
84     // always obtain the lock for the lower-numbered file handle first:
85     if (source_fh < target_fh)
86     {
87         __acrt_lowio_lock_fh(source_fh);
88         __acrt_lowio_lock_fh(target_fh);
89     }
90     else if (source_fh > target_fh)
91     {
92         __acrt_lowio_lock_fh(target_fh);
93         __acrt_lowio_lock_fh(source_fh);
94     }
95 
96     int result = 0;
97 
98     __try
99     {
100         result = _dup2_nolock_internal(source_fh, target_fh, ptd);
101     }
102     __finally
103     {
104         // The order in which we unlock the file handles does not matter:
105         __acrt_lowio_unlock_fh(source_fh);
106         __acrt_lowio_unlock_fh(target_fh);
107     }
108     __endtry
109     return result;
110 }
111 
112 // _dup2() makes the target file handle a duplicate of the source file handle,
113 // so that both handles refer to the same file.  If the target handle is open
114 // upon entry, it is closed so that it is not leaked.
115 //
116 // Returns 0 if successful; returns -1 and sets errno on failure.
117 extern "C" int __cdecl _dup2(int const source_fh, int const target_fh)
118 {
119     __crt_cached_ptd_host ptd;
120     return _dup2_internal(source_fh, target_fh, ptd);
121 }
122