virtual-image: Fix race condition closing new connection

When a new connection came in we would close the old connection. This in
turn would trigger a receive error causing the *new* connection to be
closed from the error handler.

Fix this by simply cancelling any pending transfers when a new
connection comes in. Also change the error handling code to catch issues
like partial writes correctly.

This fixes an issue for the fprintd test where some tests were flaky.
This commit is contained in:
Benjamin Berg 2020-11-10 15:56:30 +01:00
parent 251ccef9ba
commit fe498c56c7

View file

@ -73,14 +73,21 @@ recv_image_img_recv_cb (GObject *source_object,
success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error); success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error);
if (!success || bytes == 0) /* Can't use self if the operation was cancelled. */
if (!success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
device = FP_IMAGE_DEVICE (self);
/* Consider success if we received the right amount of data, otherwise
* an error must have happened. */
if (bytes < self->recv_img->width * self->recv_img->height)
{ {
if (!success) if (!success)
{ g_warning ("Error receiving image data: %s", error->message);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) else
return; g_warning ("Error receiving image data: end of stream before all data was read");
g_warning ("Error receiving header for image data: %s", error->message);
}
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
@ -88,9 +95,6 @@ recv_image_img_recv_cb (GObject *source_object,
return; return;
} }
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
device = FP_IMAGE_DEVICE (self);
if (self->automatic_finger) if (self->automatic_finger)
fpi_image_device_report_finger_status (device, TRUE); fpi_image_device_report_finger_status (device, TRUE);
fpi_image_device_image_captured (device, g_steal_pointer (&self->recv_img)); fpi_image_device_image_captured (device, g_steal_pointer (&self->recv_img));
@ -113,7 +117,7 @@ recv_image_hdr_recv_cb (GObject *source_object,
success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error); success = g_input_stream_read_all_finish (G_INPUT_STREAM (source_object), res, &bytes, &error);
if (!success || bytes == 0) if (!success || bytes != sizeof (self->recv_img_hdr))
{ {
if (!success) if (!success)
{ {
@ -122,6 +126,10 @@ recv_image_hdr_recv_cb (GObject *source_object,
return; return;
g_warning ("Error receiving header for image data: %s", error->message); g_warning ("Error receiving header for image data: %s", error->message);
} }
else if (bytes != 0)
{
g_warning ("Received incomplete header before end of stream.");
}
self = FPI_DEVICE_VIRTUAL_IMAGE (user_data); self = FPI_DEVICE_VIRTUAL_IMAGE (user_data);
g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL); g_io_stream_close (G_IO_STREAM (self->connection), NULL, NULL);
@ -235,10 +243,12 @@ new_connection_cb (GObject *source_object, GAsyncResult *res, gpointer user_data
if (dev->connection) if (dev->connection)
{ {
/* We may not have noticed that the stream was closed, /* We may not have noticed that the stream was closed,
* if the device is deactivated. Double check here. */ * if the device is deactivated.
g_input_stream_is_closed (g_io_stream_get_input_stream (G_IO_STREAM (dev->connection))); * Cancel any ongoing operation on the old connection. */
g_cancellable_cancel (dev->cancellable);
g_clear_object (&dev->cancellable);
dev->cancellable = g_cancellable_new ();
g_io_stream_close (G_IO_STREAM (dev->connection), NULL, NULL);
g_clear_object (&dev->connection); g_clear_object (&dev->connection);
} }