@@ -28,6 +28,7 @@ extern char** environ;
2828#if defined(_WIN32)
2929#include < Psapi.h>
3030#include < TlHelp32.h>
31+ #include < sddl.h>
3132#pragma comment(lib, "Advapi32")
3233#else
3334#include < fcntl.h>
@@ -57,36 +58,13 @@ namespace
5758
5859 static std::atomic_int32_t debug_id_counter{1000 };
5960
61+ #if !defined(_WIN32)
6062 struct ChildStdinTracker
6163 {
6264 StringView input;
6365 std::size_t offset;
6466
65- #if defined(_WIN32)
6667 // Write a hunk of data to `target`. If there is no more input to write, returns `true`.
67- ExpectedL<bool > do_write (HANDLE target)
68- {
69- const auto this_write = input.size () - offset;
70- if (this_write != 0 )
71- {
72- const auto this_write_clamped = static_cast <DWORD>(this_write > MAXDWORD ? MAXDWORD : this_write);
73- DWORD actually_written;
74- if (!WriteFile (target,
75- static_cast <const void *>(input.data () + offset),
76- this_write_clamped,
77- &actually_written,
78- nullptr ))
79- {
80- return format_system_error_message (" WriteFile" , GetLastError ());
81- }
82-
83- offset += actually_written;
84- }
85-
86- return offset == input.size ();
87- }
88- #else // ^^^ _WIN32 // !_WIN32 vvv
89- // Write a hunk of data to `target`. If there is no more input to write, returns `true`.
9068 ExpectedL<bool > do_write (int target)
9169 {
9270 const auto this_write = input.size () - offset;
@@ -107,8 +85,8 @@ namespace
10785
10886 return offset == input.size ();
10987 }
110- #endif // ^^^ !_WIN32
11188 };
89+ #endif // ^^^ !_WIN32
11290} // unnamed namespace
11391
11492namespace vcpkg
@@ -896,6 +874,12 @@ namespace
896874 return windows_create_process (debug_id, cmd_line, wd, env, dwCreationFlags, startup_info_ex);
897875 }
898876
877+ struct OverlappedStatus : OVERLAPPED
878+ {
879+ DWORD expected_write;
880+ HANDLE* target;
881+ };
882+
899883 struct ProcessInfoAndPipes
900884 {
901885 ProcessInfo proc_info;
@@ -916,22 +900,6 @@ namespace
916900 close_handle_mark_invalid (child_handles[1 ]);
917901 }
918902
919- void handle_child_read (ChildStdinTracker& stdin_tracker)
920- {
921- auto maybe_done = stdin_tracker.do_write (child_handles[0 ]);
922- if (auto done = maybe_done.get ())
923- {
924- if (*done)
925- {
926- close_handle_mark_invalid (child_handles[0 ]);
927- }
928-
929- return ;
930- }
931-
932- vcpkg::Checks::unreachable (VCPKG_LINE_INFO);
933- }
934-
935903 template <class Function >
936904 void handle_child_write (const Function& f, char * buf, DWORD buffer_size, Encoding encoding)
937905 {
@@ -968,34 +936,92 @@ namespace
968936 }
969937
970938 template <class Function >
971- int wait_and_stream_output (StringView input, const Function& f, Encoding encoding)
939+ int wait_and_stream_output (const char * input, DWORD input_size , const Function& f, Encoding encoding)
972940 {
973- ChildStdinTracker input_tracker{input, 0 };
941+ static const auto stdin_completion_routine =
942+ [](DWORD dwErrorCode, DWORD dwNumberOfBytesTransferred, LPOVERLAPPED pOverlapped) {
943+ const auto status = static_cast <OverlappedStatus*>(pOverlapped);
944+ switch (dwErrorCode)
945+ {
946+ case 0 :
947+ // OK, done
948+ Checks::check_exit (VCPKG_LINE_INFO, dwNumberOfBytesTransferred == status->expected_write );
949+ break ;
950+ case ERROR_BROKEN_PIPE:
951+ case ERROR_OPERATION_ABORTED:
952+ // OK, child didn't want all the data
953+ break ;
954+ default : Checks::unreachable (VCPKG_LINE_INFO, " stdin write completion" );
955+ }
956+
957+ close_handle_mark_invalid (*status->target );
958+ };
959+
960+ OverlappedStatus stdin_write{};
961+ stdin_write.expected_write = input_size;
962+ stdin_write.target = &child_handles[0 ];
963+ if (input_size == 0 )
964+ {
965+ close_handle_mark_invalid (child_handles[0 ]);
966+ }
967+ else
968+ {
969+ stdin_write.expected_write = input_size;
970+ if (WriteFileEx (child_handles[0 ], input, input_size, &stdin_write, stdin_completion_routine))
971+ {
972+ // write completed synchronously
973+ close_handle_mark_invalid (child_handles[0 ]);
974+ }
975+ else
976+ {
977+ DWORD last_error = GetLastError ();
978+ if (last_error != ERROR_IO_PENDING)
979+ {
980+ vcpkg::Checks::unreachable (VCPKG_LINE_INFO,
981+ fmt::format (" Writing stdin failed: {:x}" , last_error));
982+ }
983+ }
984+ }
985+
974986 static constexpr DWORD buffer_size = 1024 * 32 ;
975987 char buf[buffer_size];
976- while (child_handles[0 ] != INVALID_HANDLE_VALUE && child_handles[ 1 ] != INVALID_HANDLE_VALUE)
988+ while (child_handles[1 ] != INVALID_HANDLE_VALUE)
977989 {
978- switch (WaitForMultipleObjects ( 2 , child_handles, FALSE , INFINITE ))
990+ switch (WaitForSingleObjectEx ( child_handles[ 1 ], INFINITE, TRUE ))
979991 {
980- case WAIT_OBJECT_0: handle_child_read (input_tracker); break ;
981- case WAIT_OBJECT_0 + 1 : handle_child_write (f, buf, buffer_size, encoding); break ;
992+ case WAIT_OBJECT_0: handle_child_write (f, buf, buffer_size, encoding); break ;
993+ case WAIT_IO_COMPLETION:
994+ // stdin might have completed, that's OK
995+ break ;
996+ case WAIT_FAILED:
997+ vcpkg::Checks::unreachable (VCPKG_LINE_INFO,
998+ fmt::format (" Waiting for stdout failed: {:x}" , GetLastError ()));
999+ break ;
9821000 default : vcpkg::Checks::unreachable (VCPKG_LINE_INFO); break ;
9831001 }
9841002 }
9851003
986- while (child_handles[ 0 ] != INVALID_HANDLE_VALUE &&
987- WaitForSingleObject (child_handles[0 ], INFINITE) == WAIT_OBJECT_0 )
1004+ auto child_exit_code = proc_info. wait ();
1005+ if (child_handles[0 ] != INVALID_HANDLE_VALUE )
9881006 {
989- handle_child_read (input_tracker);
990- }
1007+ if (!CancelIo (child_handles[0 ]))
1008+ {
1009+ vcpkg::Checks::unreachable (VCPKG_LINE_INFO,
1010+ fmt::format (" Cancel stdin write failed: {:x}" , GetLastError ()));
1011+ }
9911012
992- while (child_handles[1 ] != INVALID_HANDLE_VALUE &&
993- WaitForSingleObject (child_handles[1 ], INFINITE) == WAIT_OBJECT_0)
994- {
995- handle_child_write (f, buf, buffer_size, encoding);
1013+ DWORD transferred;
1014+ if (GetOverlappedResult (child_handles[0 ], &stdin_write, &transferred, TRUE ))
1015+ {
1016+ stdin_completion_routine (0 , transferred, &stdin_write);
1017+ }
1018+ else
1019+ {
1020+ stdin_completion_routine (GetLastError (), transferred, &stdin_write);
1021+ }
9961022 }
9971023
998- return proc_info. wait () ;
1024+ return child_exit_code ;
9991025 }
10001026 };
10011027
@@ -1063,34 +1089,68 @@ namespace
10631089 }
10641090 };
10651091
1092+ struct CreatorOnlySecurityDescriptor
1093+ {
1094+ PSECURITY_DESCRIPTOR sd;
1095+
1096+ CreatorOnlySecurityDescriptor () : sd{}
1097+ {
1098+ // DACL:
1099+ // ACE 0: Allow; FILE_READ;;;OWNER_RIGHTS
1100+ Checks::check_exit (
1101+ VCPKG_LINE_INFO,
1102+ ConvertStringSecurityDescriptorToSecurityDescriptorW (L" D:(A;;FR;;;OW)" , SDDL_REVISION_1, &sd, 0 ));
1103+ }
1104+
1105+ ~CreatorOnlySecurityDescriptor () { LocalFree (sd); }
1106+
1107+ CreatorOnlySecurityDescriptor (const CreatorOnlySecurityDescriptor&) = delete ;
1108+ CreatorOnlySecurityDescriptor& operator =(const CreatorOnlySecurityDescriptor&) = delete ;
1109+ };
1110+
10661111 ExpectedL<ProcessInfoAndPipes> windows_create_process_redirect (std::int32_t debug_id,
10671112 StringView cmd_line,
10681113 const WorkingDirectory& wd,
10691114 const Environment& env,
10701115 DWORD dwCreationFlags) noexcept
10711116 {
1117+ static CreatorOnlySecurityDescriptor creator_owner_sd;
10721118 ProcessInfoAndPipes ret;
10731119 StartupInfoWithPipes startup_info_ex;
1074- SECURITY_ATTRIBUTES saAttr {sizeof (SECURITY_ATTRIBUTES), nullptr , TRUE };
1120+ SECURITY_ATTRIBUTES namedSa {sizeof (SECURITY_ATTRIBUTES), creator_owner_sd. sd , FALSE };
10751121
1076- // Create a pipe for the child process's STDOUT.
1077- if (!CreatePipe (&ret.child_handles [1 ], &startup_info_ex.StartupInfo .hStdOutput , &saAttr, 0 ))
1122+ // Create a pipe for the child process's STDIN.
1123+ std::wstring pipe_name{Strings::to_utf16 (
1124+ fmt::format (R"( \\.\pipe\local\vcpkg-stdin-A8B4F218-4DB1-4A3E-8E5B-C41F1633F627-{})" , debug_id))};
1125+ ret.child_handles [0 ] =
1126+ CreateNamedPipeW (pipe_name.c_str (),
1127+ PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
1128+ PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,
1129+ 1 , // nMaxInstances
1130+ 65535 , // nOutBufferSize
1131+ 0 , // nInBufferSize (unused / PIPE_ACCESS_OUTBOUND)
1132+ 0 , // nDefaultTimeout (only for WaitPipe; unused)
1133+ &namedSa);
1134+
1135+ if (ret.child_handles [0 ] == INVALID_HANDLE_VALUE)
10781136 {
1079- return format_system_error_message (" CreatePipe stdout " , GetLastError ());
1137+ return format_system_error_message (" CreateNamedPipeW stdin " , GetLastError ());
10801138 }
10811139
1082- // Create a pipe for the child process's STDIN.
1083- if (!CreatePipe (&startup_info_ex.StartupInfo .hStdInput , &ret.child_handles [0 ], &saAttr, 0 ))
1140+ SECURITY_ATTRIBUTES anonymousSa{sizeof (SECURITY_ATTRIBUTES), nullptr , TRUE };
1141+ startup_info_ex.StartupInfo .hStdInput =
1142+ CreateFileW (pipe_name.c_str (), FILE_GENERIC_READ, 0 , &anonymousSa, OPEN_EXISTING, 0 , 0 );
1143+
1144+ if (startup_info_ex.StartupInfo .hStdInput == INVALID_HANDLE_VALUE)
10841145 {
1085- return format_system_error_message (" CreatePipe stdin" , GetLastError ());
1146+ return format_system_error_message (" CreateFileW stdin" , GetLastError ());
10861147 }
10871148
1088- DWORD nonblocking_flags = PIPE_NOWAIT;
1089- if (!SetNamedPipeHandleState ( ret.child_handles [0 ], &nonblocking_flags, nullptr , nullptr ))
1149+ // Create a pipe for the child process's STDOUT.
1150+ if (!CreatePipe (& ret.child_handles [1 ], &startup_info_ex. StartupInfo . hStdOutput , &anonymousSa, 0 ))
10901151 {
1091- return format_system_error_message (" SetNamedPipeHandleState " , GetLastError ());
1152+ return format_system_error_message (" CreatePipe stdout " , GetLastError ());
10921153 }
1093-
10941154 startup_info_ex.StartupInfo .hStdError = startup_info_ex.StartupInfo .hStdOutput ;
10951155
10961156 // Ensure that only the write handle to STDOUT and the read handle to STDIN are inherited.
@@ -1441,19 +1501,26 @@ namespace vcpkg
14411501 using vcpkg::g_ctrl_c_state;
14421502
14431503 g_ctrl_c_state.transition_to_spawn_process ();
1504+ std::wstring as_utf16;
1505+ if (encoding == Encoding::Utf16)
1506+ {
1507+ as_utf16 = Strings::to_utf16 (stdin_content);
1508+ stdin_content =
1509+ StringView{reinterpret_cast <const char *>(as_utf16.data ()), as_utf16.size () * sizeof (wchar_t )};
1510+ }
1511+
1512+ auto stdin_content_size_raw = stdin_content.size ();
1513+ if (stdin_content_size_raw > MAXDWORD)
1514+ {
1515+ return format_system_error_message (" WriteFileEx" , ERROR_INSUFFICIENT_BUFFER);
1516+ }
1517+
1518+ auto stdin_content_size = static_cast <DWORD>(stdin_content_size_raw);
1519+
14441520 ExpectedL<int > exit_code =
14451521 windows_create_process_redirect (debug_id, cmd_line.command_line (), wd, env, 0 )
14461522 .map ([&](ProcessInfoAndPipes&& output) {
1447- if (encoding == Encoding::Utf16)
1448- {
1449- auto as_utf16 = Strings::to_utf16 (stdin_content);
1450- return output.wait_and_stream_output (StringView{reinterpret_cast <const char *>(as_utf16.data ()),
1451- as_utf16.size () * sizeof (wchar_t )},
1452- data_cb,
1453- encoding);
1454- }
1455-
1456- return output.wait_and_stream_output (stdin_content, data_cb, encoding);
1523+ return output.wait_and_stream_output (stdin_content.data (), stdin_content_size, data_cb, encoding);
14571524 });
14581525 g_ctrl_c_state.transition_from_spawn_process ();
14591526#else // ^^^ _WIN32 // !_WIN32 vvv
0 commit comments