From 9386a5e76016b96f17d2570b314b5d2640d9cdd8 Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Wed, 9 Jun 2010 13:33:31 +0000 Subject: [PATCH] 2010-06-09 Marcus Brinkmann * w32-io.c [HAVE_W32CE_SYSTEM]: Include assuan.h and winioctl.h. (GPGCEDEV_IOCTL_UNBLOCK) [HAVE_W32CE_SYSTEM]: Define. (set_synchronize) [HAVE_W32CE_SYSTEM]: Stub it out. (is_socket): Allow to return -1 for auto-detect (old behaviour). (is_socket) [HAVE_W32CE_SYSTEM]: Return -1. (reader): Handle auto-detect case. Handle ctx->stop_me before checking for EOF. (destroy_reader) [HAVE_W32CE_SYSTEM]: Unblock a pending reader. (writer): Handle auto-detect case. Handle ctx->stop_me with ERROR_BUSY. (destroy_writer) [HAVE_W32CE_SYSTEM]: Unblock a pending writer. (_gpgme_io_pipe) [HAVE_W32CE_SYSTEM]: Implement in terms of a half-pipe. (build_commandline) [HAVE_W32CE_SYSTEM]: New function. (_gpgme_io_spawn) [HAVE_W32CE_SYSTEM]: Implement it differently for this platform. (_gpgme_io_fd2str) [HAVE_W32CE_SYSTEM]: Implement it for RVIDs. (_gpgme_io_dup) [HAVE_W32CE_SYSTEM]: Stub it out. --- src/ChangeLog | 19 +++ src/w32-io.c | 376 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 363 insertions(+), 32 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 27fcd7c..8b8b287 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,24 @@ 2010-06-09 Marcus Brinkmann + * w32-io.c [HAVE_W32CE_SYSTEM]: Include assuan.h and winioctl.h. + (GPGCEDEV_IOCTL_UNBLOCK) [HAVE_W32CE_SYSTEM]: Define. + (set_synchronize) [HAVE_W32CE_SYSTEM]: Stub it out. + (is_socket): Allow to return -1 for auto-detect (old behaviour). + (is_socket) [HAVE_W32CE_SYSTEM]: Return -1. + (reader): Handle auto-detect case. Handle ctx->stop_me before + checking for EOF. + (destroy_reader) [HAVE_W32CE_SYSTEM]: Unblock a pending reader. + (writer): Handle auto-detect case. Handle ctx->stop_me with + ERROR_BUSY. + (destroy_writer) [HAVE_W32CE_SYSTEM]: Unblock a pending writer. + (_gpgme_io_pipe) [HAVE_W32CE_SYSTEM]: Implement in terms of a + half-pipe. + (build_commandline) [HAVE_W32CE_SYSTEM]: New function. + (_gpgme_io_spawn) [HAVE_W32CE_SYSTEM]: Implement it differently + for this platform. + (_gpgme_io_fd2str) [HAVE_W32CE_SYSTEM]: Implement it for RVIDs. + (_gpgme_io_dup) [HAVE_W32CE_SYSTEM]: Stub it out. + * gpgme-tool.c (result_add_timestamp): Add missing NULL argument. (result_sign_to_xml): Protect against NULL fingerprint. (struct server): New members input_fd, input_filename, diff --git a/src/w32-io.c b/src/w32-io.c index 79720f3..a727cfc 100644 --- a/src/w32-io.c +++ b/src/w32-io.c @@ -33,6 +33,13 @@ #include #include +#ifdef HAVE_W32CE_SYSTEM +#include +#include +#define GPGCEDEV_IOCTL_UNBLOCK \ + CTL_CODE (FILE_DEVICE_STREAMS, 2050, METHOD_BUFFERED, FILE_ANY_ACCESS) +#endif + #include "util.h" #include "sema.h" #include "priv-io.h" @@ -154,6 +161,9 @@ get_desired_thread_priority (void) static HANDLE set_synchronize (HANDLE hd) { +#ifdef HAVE_W32CE_SYSTEM + return hd; +#else HANDLE new_hd; /* For NT we have to set the sync flag. It seems that the only way @@ -171,13 +181,18 @@ set_synchronize (HANDLE hd) CloseHandle (hd); return new_hd; +#endif } -/* Return true if HD refers to a socket. */ +/* Return 1 if HD refers to a socket, 0 if it does not refer to a + socket, and -1 for unknown (autodetect). */ static int is_socket (HANDLE hd) { +#ifdef HAVE_W32CE_SYSTEM + return -1; +#else /* We need to figure out whether we are working on a socket or on a handle. A trivial way would be to check for the return code of recv and see if it is WSAENOTSOCK. However the recv may block @@ -193,12 +208,14 @@ is_socket (HANDLE hd) only if it is supported by the service provider. Tests on a stock XP using a local TCP socket show that it does not work. */ DWORD dummyflags, dummyoutsize, dummyinsize, dummyinst; + if (GetFileType (hd) == FILE_TYPE_PIPE && !GetNamedPipeInfo (hd, &dummyflags, &dummyoutsize, &dummyinsize, &dummyinst)) return 1; /* Function failed; thus we assume it is a socket. */ else return 0; /* Success; this is not a socket. */ +#endif } @@ -243,7 +260,7 @@ reader (void *arg) TRACE_LOG2 ("%s %d bytes", sock? "receiving":"reading", nbytes); - if (sock) + if (sock == -1 || sock == 1) { int n; @@ -251,6 +268,17 @@ reader (void *arg) ctx->buffer + ctx->writepos, nbytes, 0); if (n < 0) { + if (sock == -1) + { + if (WSAGetLastError () == WSAENOTSOCK) + { + sock = 0; + goto try_readfile; + } + else + sock = 1; + } + ctx->error_code = (int) WSAGetLastError (); if (ctx->error_code == ERROR_BROKEN_PIPE) { @@ -268,10 +296,13 @@ reader (void *arg) } else { + try_readfile: if (!ReadFile (ctx->file_hd, ctx->buffer + ctx->writepos, nbytes, &nread, NULL)) { ctx->error_code = (int) GetLastError (); + /* NOTE (W32CE): Do not ignore ERROR_BUSY! Check at + least stop_me if that happens. */ if (ctx->error_code == ERROR_BROKEN_PIPE) { ctx->eof = 1; @@ -285,20 +316,21 @@ reader (void *arg) break; } } + LOCK (ctx->mutex); + if (ctx->stop_me) + { + UNLOCK (ctx->mutex); + break; + } if (!nread) { ctx->eof = 1; TRACE_LOG ("got eof"); + UNLOCK (ctx->mutex); break; } TRACE_LOG1 ("got %u bytes", nread); - LOCK (ctx->mutex); - if (ctx->stop_me) - { - UNLOCK (ctx->mutex); - break; - } ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE; if (!SetEvent (ctx->have_data_ev)) TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev, @@ -403,6 +435,19 @@ destroy_reader (struct reader_context_s *ctx) SetEvent (ctx->have_space_ev); UNLOCK (ctx->mutex); +#ifdef HAVE_W32CE_SYSTEM + /* Scenario: We never create a full pipe, but already started + reading. Then we need to unblock the reader in the pipe driver + to make our reader thread notice that we want it to go away. */ + + if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK, + NULL, 0, NULL, 0, NULL, NULL)) + { + TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd, + "unblock control call failed for thread %p", ctx->thread_hd); + } +#endif + TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd, "waiting for termination of thread %p", ctx->thread_hd); WaitForSingleObject (ctx->stopped, INFINITE); @@ -603,16 +648,27 @@ writer (void *arg) /* Note that CTX->nbytes is not zero at this point, because _gpgme_io_write always writes at least 1 byte before waking us up, unless CTX->stop_me is true, which we catch above. */ - if (sock) + if (sock == -1 || sock == 1) { /* We need to try send first because a socket handle can't be used with WriteFile. */ int n; - + n = send (handle_to_socket (ctx->file_hd), ctx->buffer, ctx->nbytes, 0); if (n < 0) { + if (sock == -1) + { + if (WSAGetLastError () == WSAENOTSOCK) + { + sock = 0; + goto try_writefile; + } + else + sock = 1; + } + ctx->error_code = (int) WSAGetLastError (); ctx->error = 1; TRACE_LOG1 ("send error: ec=%d", ctx->error_code); @@ -622,9 +678,17 @@ writer (void *arg) } else { + try_writefile: if (!WriteFile (ctx->file_hd, ctx->buffer, ctx->nbytes, &nwritten, NULL)) { + if (GetLastError () == ERROR_BUSY) + { + /* Probably stop_me is set now. */ + TRACE_LOG ("pipe busy (unblocked?)"); + continue; + } + ctx->error_code = (int) GetLastError (); ctx->error = 1; TRACE_LOG1 ("write error: ec=%d", ctx->error_code); @@ -732,6 +796,19 @@ destroy_writer (struct writer_context_s *ctx) if (ctx->have_data) SetEvent (ctx->have_data); UNLOCK (ctx->mutex); + +#ifdef HAVE_W32CE_SYSTEM + /* Scenario: We never create a full pipe, but already started + reading. Then we need to unblock the reader in the pipe driver + to make our reader thread notice that we want it to go away. */ + + if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK, + NULL, 0, NULL, 0, NULL, NULL)) + { + TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd, + "unblock control call failed for thread %p", ctx->thread_hd); + } +#endif TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd, "waiting for termination of thread %p", ctx->thread_hd); @@ -894,11 +971,40 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx) { HANDLE rh; HANDLE wh; - SECURITY_ATTRIBUTES sec_attr; + +#ifdef HAVE_W32CE_SYSTEM + HANDLE hd; + int rvid; + TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes, "inherit_idx=%i (GPGME uses it for %s)", inherit_idx, inherit_idx ? "reading" : "writing"); + hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx); + if (hd == INVALID_HANDLE_VALUE) + { + TRACE_LOG1 ("_assuan_w32ce_prepare_pipe failed: ec=%d", + (int) GetLastError ()); + /* FIXME: Should translate the error code. */ + gpg_err_set_errno (EIO); + return TRACE_SYSRES (-1); + } + + if (inherit_idx == 0) + { + /* FIXME: For now. We need to detect them at close. */ + rh = (void*) ((rvid << 1) | 1); + wh = hd; + } + else + { + rh = hd; + /* FIXME: For now. We need to detect them at close. */ + wh = (void*) ((rvid << 1) | 1); + } +#else + SECURITY_ATTRIBUTES sec_attr; + memset (&sec_attr, 0, sizeof (sec_attr)); sec_attr.nLength = sizeof (sec_attr); sec_attr.bInheritHandle = FALSE; @@ -914,7 +1020,6 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx) /* Make one end inheritable. */ if (inherit_idx == 0) { - struct writer_context_s *ctx; HANDLE hd; if (!DuplicateHandle (GetCurrentProcess(), rh, GetCurrentProcess(), &hd, 0, @@ -930,22 +1035,9 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx) } CloseHandle (rh); rh = hd; - - ctx = find_writer (handle_to_fd (wh), 0); - assert (ctx == NULL); - ctx = find_writer (handle_to_fd (wh), 1); - if (!ctx) - { - CloseHandle (rh); - CloseHandle (wh); - /* FIXME: Should translate the error code. */ - gpg_err_set_errno (EIO); - return TRACE_SYSRES (-1); - } } else if (inherit_idx == 1) { - struct reader_context_s *ctx; HANDLE hd; if (!DuplicateHandle( GetCurrentProcess(), wh, GetCurrentProcess(), &hd, 0, @@ -961,14 +1053,38 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx) } CloseHandle (wh); wh = hd; + } +#endif + if (inherit_idx == 0) + { + struct writer_context_s *ctx; + ctx = find_writer (handle_to_fd (wh), 0); + assert (ctx == NULL); + ctx = find_writer (handle_to_fd (wh), 1); + if (!ctx) + { +#ifndef HAVE_W32CE_SYSTEM + CloseHandle (rh); +#endif + CloseHandle (wh); + /* FIXME: Should translate the error code. */ + gpg_err_set_errno (EIO); + return TRACE_SYSRES (-1); + } + } + else if (inherit_idx == 1) + { + struct reader_context_s *ctx; ctx = find_reader (handle_to_fd (rh), 0); assert (ctx == NULL); ctx = find_reader (handle_to_fd (rh), 1); if (!ctx) { CloseHandle (rh); +#ifndef HAVE_W32CE_SYSTEM CloseHandle (wh); +#endif /* FIXME: Should translate the error code. */ gpg_err_set_errno (EIO); return TRACE_SYSRES (-1); @@ -995,6 +1111,12 @@ _gpgme_io_close (int fd) return TRACE_SYSRES (-1); } +#ifdef HAVE_W32CE_SYSTEM + /* FIXME: For now: This is a rendezvous id. */ + if (fd & 1) + return TRACE_SYSRES (0); +#endif + kill_reader (fd); kill_writer (fd); LOCK (notify_table_lock); @@ -1066,7 +1188,105 @@ _gpgme_io_set_nonblocking (int fd) return 0; } +#ifdef HAVE_W32CE_SYSTEM +static char * +build_commandline (char **argv, int fd0, int fd0_isnull, + int fd1, int fd1_isnull, + int fd2, int fd2_isnull) +{ + int i, n; + const char *s; + char *buf, *p; + char fdbuf[3*30]; + + p = fdbuf; + *p = 0; + + strcpy (p, "-&S0=null "); + p += strlen (p); + if (fd0 != -1) + { + /* FIXME */ + if (fd0 & 1) + fd0 = fd0 >> 1; + + if (fd0_isnull) + strcpy (p, "-&S0=null "); + else + snprintf (p, 25, "-&S0=%d ", (int)fd0); + p += strlen (p); + } + if (fd1 != -1) + { + /* FIXME */ + if (fd1 & 1) + fd1 = fd1 >> 1; + + if (fd1_isnull) + strcpy (p, "-&S1=null "); + else + snprintf (p, 25, "-&S1=%d ", (int)fd1); + p += strlen (p); + } + if (fd2 != -1) + { + /* FIXME */ + if (fd2 & 1) + fd2 = fd2 >> 1; + if (fd2_isnull) + strcpy (p, "-&S2=null "); + else + snprintf (p, 25, "-&S2=%d ", (int)fd2); + p += strlen (p); + } + strcpy (p, "-&S2=null "); + p += strlen (p); + + n = strlen (fdbuf); + for (i=0; (s = argv[i]); i++) + { + if (!i) + continue; /* Ignore argv[0]. */ + n += strlen (s) + 1 + 2; /* (1 space, 2 quoting) */ + for (; *s; s++) + if (*s == '\"') + n++; /* Need to double inner quotes. */ + } + n++; + buf = p = malloc (n); + if (! buf) + return NULL; + + p = stpcpy (p, fdbuf); + for (i = 0; argv[i]; i++) + { + if (!i) + continue; /* Ignore argv[0]. */ + if (i > 1) + p = stpcpy (p, " "); + + if (! *argv[i]) /* Empty string. */ + p = stpcpy (p, "\"\""); + else if (strpbrk (argv[i], " \t\n\v\f\"")) + { + p = stpcpy (p, "\""); + for (s = argv[i]; *s; s++) + { + *p++ = *s; + if (*s == '\"') + *p++ = *s; + } + *p++ = '\"'; + *p = 0; + } + else + p = stpcpy (p, argv[i]); + } + + return buf; +} +#else static char * build_commandline (char **argv) { @@ -1120,6 +1340,7 @@ build_commandline (char **argv) return buf; } +#endif int @@ -1128,7 +1349,6 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, void (*atfork) (void *opaque, int reserved), void *atforkvalue, pid_t *r_pid) { - SECURITY_ATTRIBUTES sec_attr; PROCESS_INFORMATION pi = { NULL, /* returns process handle */ @@ -1136,10 +1356,85 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, 0, /* returns pid */ 0 /* returns tid */ }; - STARTUPINFO si; - int cr_flags = (CREATE_DEFAULT_ERROR_MODE - | GetPriorityClass (GetCurrentProcess ())); int i; + +#ifdef HAVE_W32CE_SYSTEM + int fd_in = -1; + int fd_out = -1; + int fd_err = -1; + int fd_in_isnull = 1; + int fd_out_isnull = 1; + int fd_err_isnull = 1; + char *cmdline; + + TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path, + "path=%s", path); + i = 0; + while (argv[i]) + { + TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]); + i++; + } + + for (i = 0; fd_list[i].fd != -1; i++) + { + TRACE_LOG3 ("fd_list[%2i] = fd %i, dup_to %i", i, fd_list[i].fd, fd_list[i].dup_to); + if (fd_list[i].dup_to == 0) + { + fd_in = fd_list[i].fd; + fd_in_isnull = 0; + } + else if (fd_list[i].dup_to == 1) + { + fd_out = fd_list[i].fd; + fd_out_isnull = 0; + } + else if (fd_list[i].dup_to == 2) + { + fd_err = fd_list[i].fd; + fd_err_isnull = 0; + } + } + + cmdline = build_commandline (argv, fd_in, fd_in_isnull, + fd_out, fd_out_isnull, fd_err, fd_err_isnull); + if (!cmdline) + { + TRACE_LOG1 ("build_commandline failed: %s", strerror (errno)); + return TRACE_SYSRES (-1); + } + + fprintf (stderr, "SPAWNY: %s\n", cmdline); + + if (!CreateProcessA (path, /* Program to start. */ + cmdline, /* Command line arguments. */ + NULL, /* (not supported) */ + NULL, /* (not supported) */ + FALSE, /* (not supported) */ + (CREATE_SUSPENDED), /* Creation flags. */ + NULL, /* (not supported) */ + NULL, /* (not supported) */ + NULL, /* (not supported) */ + &pi /* Returns process information.*/ + )) + { + TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ()); + free (cmdline); + gpg_err_set_errno (EIO); + return TRACE_SYSRES (-1); + } + + /* Insert the inherited handles. */ + for (i = 0; fd_list[i].fd != -1; i++) + { + /* Return the child name of this handle. */ + fd_list[i].peer_name = fd_list[i].fd; + } + +#else + SECURITY_ATTRIBUTES sec_attr; + STARTUPINFOA si; + int cr_flags = CREATE_DEFAULT_ERROR_MODE; char **args; char *arg_string; /* FIXME. */ @@ -1183,7 +1478,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, if (!arg_string) { close (tmp_fd); - DeleteFile (tmp_name); + DeleteFileA (tmp_name); return TRACE_SYSRES (-1); } @@ -1196,7 +1491,10 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, si.hStdError = INVALID_HANDLE_VALUE; cr_flags |= CREATE_SUSPENDED; +#ifndef HAVE_W32CE_SYSTEM cr_flags |= DETACHED_PROCESS; + cr_flags |= GetPriorityClass (GetCurrentProcess ()); +#endif if (!CreateProcessA (_gpgme_get_w32spawn_path (), arg_string, &sec_attr, /* process security attributes */ @@ -1211,7 +1509,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ()); free (arg_string); close (tmp_fd); - DeleteFile (tmp_name); + DeleteFileA (tmp_name); /* FIXME: Should translate the error code. */ gpg_err_set_errno (EIO); @@ -1241,7 +1539,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, CloseHandle (pi.hProcess); close (tmp_fd); - DeleteFile (tmp_name); + DeleteFileA (tmp_name); /* FIXME: Should translate the error code. */ gpg_err_set_errno (EIO); @@ -1294,6 +1592,8 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, close (tmp_fd); /* The temporary file is deleted by the gpgme-w32spawn process (hopefully). */ +#endif + TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, " "dwProcessID=%d, dwThreadId=%d", @@ -1511,6 +1811,13 @@ _gpgme_io_subsystem_init (void) int _gpgme_io_fd2str (char *buf, int buflen, int fd) { +#ifdef HAVE_W32CE_SYSTEM + /* FIXME: For now. See above. */ + if (fd & 1) + fd = fd >> 1; + /* FIXME: The real problems start if fd is not of this type! */ +#endif + return snprintf (buf, buflen, "%d", fd); } @@ -1518,6 +1825,10 @@ _gpgme_io_fd2str (char *buf, int buflen, int fd) int _gpgme_io_dup (int fd) { +#ifdef HAVE_W32CE_SYSTEM + gpg_err_set_errno (EIO); + return -1; +#else HANDLE handle = fd_to_handle (fd); HANDLE new_handle = fd_to_handle (fd); int i; @@ -1575,6 +1886,7 @@ _gpgme_io_dup (int fd) } return TRACE_SYSRES (handle_to_fd (new_handle)); +#endif } -- 2.26.2