From 99125e46eac844b79c60406de56b2ac7f852e332 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 20 Jan 2022 17:31:16 +0000 Subject: [PATCH 01/42] - Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- libusb1.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libusb1.spec b/libusb1.spec index b3bee37..804d0f2 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -1,7 +1,7 @@ Summary: Library for accessing USB devices Name: libusb1 Version: 1.0.24 -Release: 4%{?dist} +Release: 5%{?dist} Source0: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2 License: LGPLv2+ URL: http://libusb.info @@ -123,6 +123,9 @@ LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-example-listdevs %changelog +* Thu Jan 20 2022 Fedora Release Engineering - 1.0.24-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + * Thu Jul 22 2021 Fedora Release Engineering - 1.0.24-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild From 608ae59ae99132bd1d12831a50780372d53d190a Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 2 Feb 2022 16:47:15 +0100 Subject: [PATCH 02/42] Update to 1.0.25 --- .gitignore | 1 + ...pt-sysfs-attributes-not-terminated-w.patch | 60 ----- ...parsing-of-descriptors-for-multi-con.patch | 61 ----- ...efully-handle-buggy-devices-with-a-c.patch | 220 ------------------ changelog | 9 + libusb1.spec | 17 +- sources | 2 +- 7 files changed, 14 insertions(+), 356 deletions(-) delete mode 100644 0001-linux_usbfs-Accept-sysfs-attributes-not-terminated-w.patch delete mode 100644 0001-linux_usbfs-Fix-parsing-of-descriptors-for-multi-con.patch delete mode 100644 0002-linux_usbfs-Gracefully-handle-buggy-devices-with-a-c.patch create mode 100644 changelog diff --git a/.gitignore b/.gitignore index 65a24a8..3a9ae78 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /libusb-1.0.24.tar.bz2 +/libusb-1.0.25.tar.bz2 diff --git a/0001-linux_usbfs-Accept-sysfs-attributes-not-terminated-w.patch b/0001-linux_usbfs-Accept-sysfs-attributes-not-terminated-w.patch deleted file mode 100644 index 822fbe0..0000000 --- a/0001-linux_usbfs-Accept-sysfs-attributes-not-terminated-w.patch +++ /dev/null @@ -1,60 +0,0 @@ -From c486d01297a366aae8dcd3f715d0bfd8b995949b Mon Sep 17 00:00:00 2001 -From: Chris Dickens -Date: Mon, 8 Feb 2021 09:27:20 -0800 -Subject: [PATCH 1/2] linux_usbfs: Accept sysfs attributes not terminated with - newline - -Benjamin Berg reports that some CI systems that simulate sysfs devices -are causing libusb to report errors because such systems are not -conforming to the sysfs pattern of terminating attribute values with a -newline character. Reduce the severity of encountering such -non-conforming attibute values from an error that prevents enumeration -to a warning message. - -Closes #857 - -Signed-off-by: Chris Dickens ---- - libusb/os/linux_usbfs.c | 12 +++++++----- - libusb/version_nano.h | 2 +- - 2 files changed, 8 insertions(+), 6 deletions(-) - -diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c -index 4882c0f..ebf8cfe 100644 ---- a/libusb/os/linux_usbfs.c -+++ b/libusb/os/linux_usbfs.c -@@ -505,7 +505,7 @@ static int read_sysfs_attr(struct libusb_context *ctx, - if (fd < 0) - return fd; - -- r = read(fd, buf, sizeof(buf)); -+ r = read(fd, buf, sizeof(buf) - 1); - if (r < 0) { - r = errno; - close(fd); -@@ -523,16 +523,18 @@ static int read_sysfs_attr(struct libusb_context *ctx, - return 0; - } - -- /* The kernel does *not* NULL-terminate the string, but every attribute -+ /* The kernel does *not* NUL-terminate the string, but every attribute - * should be terminated with a newline character. */ - if (!isdigit(buf[0])) { - usbi_err(ctx, "attribute %s doesn't have numeric value?", attr); - return LIBUSB_ERROR_IO; - } else if (buf[r - 1] != '\n') { -- usbi_err(ctx, "attribute %s doesn't end with newline?", attr); -- return LIBUSB_ERROR_IO; -+ usbi_warn(ctx, "attribute %s doesn't end with newline?", attr); -+ } else { -+ /* Remove the terminating newline character */ -+ r--; - } -- buf[r - 1] = '\0'; -+ buf[r] = '\0'; - - errno = 0; - value = strtol(buf, &endptr, 10); --- -2.29.2 - diff --git a/0001-linux_usbfs-Fix-parsing-of-descriptors-for-multi-con.patch b/0001-linux_usbfs-Fix-parsing-of-descriptors-for-multi-con.patch deleted file mode 100644 index 1908bf4..0000000 --- a/0001-linux_usbfs-Fix-parsing-of-descriptors-for-multi-con.patch +++ /dev/null @@ -1,61 +0,0 @@ -From f6d2cb561402c3b6d3627c0eb89e009b503d9067 Mon Sep 17 00:00:00 2001 -From: Chris Dickens -Date: Sun, 13 Dec 2020 15:49:19 -0800 -Subject: [PATCH] linux_usbfs: Fix parsing of descriptors for - multi-configuration devices - -Commit e2be556bd2 ("linux_usbfs: Parse config descriptors during device -initialization") introduced a regression for devices with multiple -configurations. The logic that verifies the reported length of the -configuration descriptors failed to count the length of the -configuration descriptor itself and would truncate the actual length by -9 bytes, leading to a parsing error for subsequent descriptors. - -Closes #825 - -Signed-off-by: Chris Dickens ---- - libusb/os/linux_usbfs.c | 12 ++++++++---- - libusb/version_nano.h | 2 +- - 2 files changed, 9 insertions(+), 5 deletions(-) - -diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c -index fb2ed53..4d2dc8d 100644 ---- a/libusb/os/linux_usbfs.c -+++ b/libusb/os/linux_usbfs.c -@@ -641,7 +641,12 @@ static int seek_to_next_config(struct libusb_context *ctx, - uint8_t *buffer, size_t len) - { - struct usbi_descriptor_header *header; -- int offset = 0; -+ int offset; -+ -+ /* Start seeking past the config descriptor */ -+ offset = LIBUSB_DT_CONFIG_SIZE; -+ buffer += LIBUSB_DT_CONFIG_SIZE; -+ len -= LIBUSB_DT_CONFIG_SIZE; - - while (len > 0) { - if (len < 2) { -@@ -718,7 +723,7 @@ static int parse_config_descriptors(struct libusb_device *dev) - } - - if (priv->sysfs_dir) { -- /* -+ /* - * In sysfs wTotalLength is ignored, instead the kernel returns a - * config descriptor with verified bLength fields, with descriptors - * with an invalid bLength removed. -@@ -727,8 +732,7 @@ static int parse_config_descriptors(struct libusb_device *dev) - int offset; - - if (num_configs > 1 && idx < num_configs - 1) { -- offset = seek_to_next_config(ctx, buffer + LIBUSB_DT_CONFIG_SIZE, -- remaining - LIBUSB_DT_CONFIG_SIZE); -+ offset = seek_to_next_config(ctx, buffer, remaining); - if (offset < 0) - return offset; - sysfs_config_len = (uint16_t)offset; --- -2.29.2 - diff --git a/0002-linux_usbfs-Gracefully-handle-buggy-devices-with-a-c.patch b/0002-linux_usbfs-Gracefully-handle-buggy-devices-with-a-c.patch deleted file mode 100644 index b95a501..0000000 --- a/0002-linux_usbfs-Gracefully-handle-buggy-devices-with-a-c.patch +++ /dev/null @@ -1,220 +0,0 @@ -From f38f09da98acc63966b65b72029b1f7f81166bef Mon Sep 17 00:00:00 2001 -From: Chris Dickens -Date: Mon, 8 Feb 2021 11:56:13 -0800 -Subject: [PATCH 2/2] linux_usbfs: Gracefully handle buggy devices with a - configuration 0 - -The USB spec states that a configuration value of 0 is reserved and is -used to indicate the device in not configured (e.g. is in the address -state). Unfortunately some devices do exist that violate this and use 0 -as the bConfigurationValue of the configuration descriptor. - -Improve how the Linux backend handles such non-conformant devices by -adding special handling around the configuration value 0. Most devices -will not require this special handling, but for those that do there is -no way to distinguish between the device being unconfigured and using -configuration 0. - -Closes #850 - -Signed-off-by: Chris Dickens ---- - libusb/os/linux_usbfs.c | 94 ++++++++++++++++++++++++++--------------- - libusb/version_nano.h | 2 +- - 2 files changed, 60 insertions(+), 36 deletions(-) - -diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c -index ebf8cfe..3a1894c 100644 ---- a/libusb/os/linux_usbfs.c -+++ b/libusb/os/linux_usbfs.c -@@ -128,7 +128,7 @@ struct linux_device_priv { - void *descriptors; - size_t descriptors_len; - struct config_descriptor *config_descriptors; -- uint8_t active_config; /* cache val for !sysfs_available */ -+ int active_config; /* cache val for !sysfs_available */ - }; - - struct linux_device_handle_priv { -@@ -169,6 +169,21 @@ struct linux_transfer_priv { - int iso_packet_offset; - }; - -+static int dev_has_config0(struct libusb_device *dev) -+{ -+ struct linux_device_priv *priv = usbi_get_device_priv(dev); -+ struct config_descriptor *config; -+ uint8_t idx; -+ -+ for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) { -+ config = &priv->config_descriptors[idx]; -+ if (config->desc->bConfigurationValue == 0) -+ return 1; -+ } -+ -+ return 0; -+} -+ - static int get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent) - { - struct libusb_context *ctx = DEVICE_CTX(dev); -@@ -574,22 +589,12 @@ static int sysfs_scan_device(struct libusb_context *ctx, const char *devname) - } - - /* read the bConfigurationValue for a device */ --static int sysfs_get_active_config(struct libusb_device *dev, uint8_t *config) -+static int sysfs_get_active_config(struct libusb_device *dev, int *config) - { - struct linux_device_priv *priv = usbi_get_device_priv(dev); -- int ret, tmp; -- -- ret = read_sysfs_attr(DEVICE_CTX(dev), priv->sysfs_dir, "bConfigurationValue", -- UINT8_MAX, &tmp); -- if (ret < 0) -- return ret; - -- if (tmp == -1) -- tmp = 0; /* unconfigured */ -- -- *config = (uint8_t)tmp; -- -- return 0; -+ return read_sysfs_attr(DEVICE_CTX(dev), priv->sysfs_dir, "bConfigurationValue", -+ UINT8_MAX, config); - } - - int linux_get_device_address(struct libusb_context *ctx, int detached, -@@ -765,6 +770,9 @@ static int parse_config_descriptors(struct libusb_device *dev) - } - } - -+ if (config_desc->bConfigurationValue == 0) -+ usbi_warn(ctx, "device has configuration 0"); -+ - priv->config_descriptors[idx].desc = config_desc; - priv->config_descriptors[idx].actual_len = config_len; - -@@ -798,7 +806,7 @@ static int op_get_active_config_descriptor(struct libusb_device *dev, - { - struct linux_device_priv *priv = usbi_get_device_priv(dev); - void *config_desc; -- uint8_t active_config; -+ int active_config; - int r; - - if (priv->sysfs_dir) { -@@ -810,12 +818,12 @@ static int op_get_active_config_descriptor(struct libusb_device *dev, - active_config = priv->active_config; - } - -- if (active_config == 0) { -+ if (active_config == -1) { - usbi_err(DEVICE_CTX(dev), "device unconfigured"); - return LIBUSB_ERROR_NOT_FOUND; - } - -- r = op_get_config_descriptor_by_value(dev, active_config, &config_desc); -+ r = op_get_config_descriptor_by_value(dev, (uint8_t)active_config, &config_desc); - if (r < 0) - return r; - -@@ -863,17 +871,26 @@ static int usbfs_get_active_config(struct libusb_device *dev, int fd) - - /* we hit this error path frequently with buggy devices :( */ - usbi_warn(DEVICE_CTX(dev), "get configuration failed, errno=%d", errno); -+ -+ /* assume the current configuration is the first one if we have -+ * the configuration descriptors, otherwise treat the device -+ * as unconfigured. */ -+ if (priv->config_descriptors) -+ priv->active_config = (int)priv->config_descriptors[0].desc->bConfigurationValue; -+ else -+ priv->active_config = -1; - } else if (active_config == 0) { -- /* some buggy devices have a configuration 0, but we're -- * reaching into the corner of a corner case here, so let's -- * not support buggy devices in these circumstances. -- * stick to the specs: a configuration value of 0 means -- * unconfigured. */ -- usbi_warn(DEVICE_CTX(dev), "active cfg 0? assuming unconfigured device"); -+ if (dev_has_config0(dev)) { -+ /* some buggy devices have a configuration 0, but we're -+ * reaching into the corner of a corner case here. */ -+ priv->active_config = 0; -+ } else { -+ priv->active_config = -1; -+ } -+ } else { -+ priv->active_config = (int)active_config; - } - -- priv->active_config = active_config; -- - return LIBUSB_SUCCESS; - } - -@@ -1004,9 +1021,9 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum, - usbi_warn(ctx, "Missing rw usbfs access; cannot determine " - "active configuration descriptor"); - if (priv->config_descriptors) -- priv->active_config = priv->config_descriptors[0].desc->bConfigurationValue; -+ priv->active_config = (int)priv->config_descriptors[0].desc->bConfigurationValue; - else -- priv->active_config = 0; /* No config dt */ -+ priv->active_config = -1; /* No config dt */ - - return LIBUSB_SUCCESS; - } -@@ -1428,22 +1445,27 @@ static int op_get_configuration(struct libusb_device_handle *handle, - uint8_t *config) - { - struct linux_device_priv *priv = usbi_get_device_priv(handle->dev); -+ int active_config; - int r; - - if (priv->sysfs_dir) { -- r = sysfs_get_active_config(handle->dev, config); -+ r = sysfs_get_active_config(handle->dev, &active_config); - } else { - struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); - - r = usbfs_get_active_config(handle->dev, hpriv->fd); - if (r == LIBUSB_SUCCESS) -- *config = priv->active_config; -+ active_config = priv->active_config; - } - if (r < 0) - return r; - -- if (*config == 0) -- usbi_err(HANDLE_CTX(handle), "device unconfigured"); -+ if (active_config == -1) { -+ usbi_warn(HANDLE_CTX(handle), "device unconfigured"); -+ active_config = 0; -+ } -+ -+ *config = (uint8_t)active_config; - - return 0; - } -@@ -1467,11 +1489,13 @@ static int op_set_configuration(struct libusb_device_handle *handle, int config) - return LIBUSB_ERROR_OTHER; - } - -- if (config == -1) -- config = 0; -+ /* if necessary, update our cached active config descriptor */ -+ if (!priv->sysfs_dir) { -+ if (config == 0 && !dev_has_config0(handle->dev)) -+ config = -1; - -- /* update our cached active config descriptor */ -- priv->active_config = (uint8_t)config; -+ priv->active_config = config; -+ } - - return LIBUSB_SUCCESS; - } --- -2.29.2 - diff --git a/changelog b/changelog new file mode 100644 index 0000000..811f64d --- /dev/null +++ b/changelog @@ -0,0 +1,9 @@ +* Thu Jan 20 2022 Fedora Release Engineering - 1.0.24-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Thu Jul 22 2021 Fedora Release Engineering - 1.0.24-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Tue Jan 19 18:47:55 CET 2021 Benjamin Berg - 1.0.24-3 +- New libusb1 package replacing libusbx + Resolves: #1918269 diff --git a/libusb1.spec b/libusb1.spec index 804d0f2..8eb7ef5 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -1,7 +1,7 @@ Summary: Library for accessing USB devices Name: libusb1 -Version: 1.0.24 -Release: 5%{?dist} +Version: 1.0.25 +Release: %autorelease Source0: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2 License: LGPLv2+ URL: http://libusb.info @@ -12,9 +12,6 @@ BuildRequires: gcc Provides: libusbx = %{version}-%{release} Obsoletes: libusbx < %{version}-%{release} -Patch001: 0001-linux_usbfs-Accept-sysfs-attributes-not-terminated-w.patch -Patch002: 0001-linux_usbfs-Fix-parsing-of-descriptors-for-multi-con.patch -Patch003: 0002-linux_usbfs-Gracefully-handle-buggy-devices-with-a-c.patch %description This package provides a way for applications to access USB devices. @@ -123,12 +120,4 @@ LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-example-listdevs %changelog -* Thu Jan 20 2022 Fedora Release Engineering - 1.0.24-5 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild - -* Thu Jul 22 2021 Fedora Release Engineering - 1.0.24-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild - -* Tue Jan 19 18:47:55 CET 2021 Benjamin Berg - 1.0.24-3 -- New libusb1 package replacing libusbx - Resolves: #1918269 \ No newline at end of file +%autochangelog diff --git a/sources b/sources index 9d92dff..75982ca 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (libusb-1.0.24.tar.bz2) = 5aea36a530aaa15c6dd656d0ed3ce204522c9946d8d39ffbb290dab4a98cda388a2598da4995123d1032324056090bd429e702459626d3e8d7daeebc4e7ff3dc +SHA512 (libusb-1.0.25.tar.bz2) = f1e6e5577d4bd1ff136927dc66c615014a06ac332ddd797b1d1ad5f7b68e2405e66068dcb210e2f0ae3e31681603ef72efbd88bf7fbe0eb41ce700fdc3f92f9d From 300a0883239bf5928b1ee2e7511ed567a56039c2 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 2 Feb 2022 19:08:45 +0100 Subject: [PATCH 03/42] Switch to rpmautospec --- changelog | 6 ++++++ libusb1.spec | 9 ++------- 2 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 changelog diff --git a/changelog b/changelog new file mode 100644 index 0000000..293a1de --- /dev/null +++ b/changelog @@ -0,0 +1,6 @@ +* Thu Jul 22 2021 Fedora Release Engineering - 1.0.24-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Tue Jan 19 18:47:55 CET 2021 Benjamin Berg - 1.0.24-3 +- New libusb1 package replacing libusbx + Resolves: #1918269 diff --git a/libusb1.spec b/libusb1.spec index b3bee37..4ec2a3d 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -1,7 +1,7 @@ Summary: Library for accessing USB devices Name: libusb1 Version: 1.0.24 -Release: 4%{?dist} +Release: %autorelease Source0: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2 License: LGPLv2+ URL: http://libusb.info @@ -123,9 +123,4 @@ LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-example-listdevs %changelog -* Thu Jul 22 2021 Fedora Release Engineering - 1.0.24-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild - -* Tue Jan 19 18:47:55 CET 2021 Benjamin Berg - 1.0.24-3 -- New libusb1 package replacing libusbx - Resolves: #1918269 \ No newline at end of file +%autochangelog From 653f1fe105e9de997c9840e02a81d52a4a53de0c Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 2 Feb 2022 19:09:09 +0100 Subject: [PATCH 04/42] Update to 1.0.25 --- .gitignore | 1 + ...pt-sysfs-attributes-not-terminated-w.patch | 60 ----- ...parsing-of-descriptors-for-multi-con.patch | 61 ----- ...efully-handle-buggy-devices-with-a-c.patch | 220 ------------------ libusb1.spec | 5 +- sources | 2 +- 6 files changed, 3 insertions(+), 346 deletions(-) delete mode 100644 0001-linux_usbfs-Accept-sysfs-attributes-not-terminated-w.patch delete mode 100644 0001-linux_usbfs-Fix-parsing-of-descriptors-for-multi-con.patch delete mode 100644 0002-linux_usbfs-Gracefully-handle-buggy-devices-with-a-c.patch diff --git a/.gitignore b/.gitignore index 65a24a8..3a9ae78 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /libusb-1.0.24.tar.bz2 +/libusb-1.0.25.tar.bz2 diff --git a/0001-linux_usbfs-Accept-sysfs-attributes-not-terminated-w.patch b/0001-linux_usbfs-Accept-sysfs-attributes-not-terminated-w.patch deleted file mode 100644 index 822fbe0..0000000 --- a/0001-linux_usbfs-Accept-sysfs-attributes-not-terminated-w.patch +++ /dev/null @@ -1,60 +0,0 @@ -From c486d01297a366aae8dcd3f715d0bfd8b995949b Mon Sep 17 00:00:00 2001 -From: Chris Dickens -Date: Mon, 8 Feb 2021 09:27:20 -0800 -Subject: [PATCH 1/2] linux_usbfs: Accept sysfs attributes not terminated with - newline - -Benjamin Berg reports that some CI systems that simulate sysfs devices -are causing libusb to report errors because such systems are not -conforming to the sysfs pattern of terminating attribute values with a -newline character. Reduce the severity of encountering such -non-conforming attibute values from an error that prevents enumeration -to a warning message. - -Closes #857 - -Signed-off-by: Chris Dickens ---- - libusb/os/linux_usbfs.c | 12 +++++++----- - libusb/version_nano.h | 2 +- - 2 files changed, 8 insertions(+), 6 deletions(-) - -diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c -index 4882c0f..ebf8cfe 100644 ---- a/libusb/os/linux_usbfs.c -+++ b/libusb/os/linux_usbfs.c -@@ -505,7 +505,7 @@ static int read_sysfs_attr(struct libusb_context *ctx, - if (fd < 0) - return fd; - -- r = read(fd, buf, sizeof(buf)); -+ r = read(fd, buf, sizeof(buf) - 1); - if (r < 0) { - r = errno; - close(fd); -@@ -523,16 +523,18 @@ static int read_sysfs_attr(struct libusb_context *ctx, - return 0; - } - -- /* The kernel does *not* NULL-terminate the string, but every attribute -+ /* The kernel does *not* NUL-terminate the string, but every attribute - * should be terminated with a newline character. */ - if (!isdigit(buf[0])) { - usbi_err(ctx, "attribute %s doesn't have numeric value?", attr); - return LIBUSB_ERROR_IO; - } else if (buf[r - 1] != '\n') { -- usbi_err(ctx, "attribute %s doesn't end with newline?", attr); -- return LIBUSB_ERROR_IO; -+ usbi_warn(ctx, "attribute %s doesn't end with newline?", attr); -+ } else { -+ /* Remove the terminating newline character */ -+ r--; - } -- buf[r - 1] = '\0'; -+ buf[r] = '\0'; - - errno = 0; - value = strtol(buf, &endptr, 10); --- -2.29.2 - diff --git a/0001-linux_usbfs-Fix-parsing-of-descriptors-for-multi-con.patch b/0001-linux_usbfs-Fix-parsing-of-descriptors-for-multi-con.patch deleted file mode 100644 index 1908bf4..0000000 --- a/0001-linux_usbfs-Fix-parsing-of-descriptors-for-multi-con.patch +++ /dev/null @@ -1,61 +0,0 @@ -From f6d2cb561402c3b6d3627c0eb89e009b503d9067 Mon Sep 17 00:00:00 2001 -From: Chris Dickens -Date: Sun, 13 Dec 2020 15:49:19 -0800 -Subject: [PATCH] linux_usbfs: Fix parsing of descriptors for - multi-configuration devices - -Commit e2be556bd2 ("linux_usbfs: Parse config descriptors during device -initialization") introduced a regression for devices with multiple -configurations. The logic that verifies the reported length of the -configuration descriptors failed to count the length of the -configuration descriptor itself and would truncate the actual length by -9 bytes, leading to a parsing error for subsequent descriptors. - -Closes #825 - -Signed-off-by: Chris Dickens ---- - libusb/os/linux_usbfs.c | 12 ++++++++---- - libusb/version_nano.h | 2 +- - 2 files changed, 9 insertions(+), 5 deletions(-) - -diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c -index fb2ed53..4d2dc8d 100644 ---- a/libusb/os/linux_usbfs.c -+++ b/libusb/os/linux_usbfs.c -@@ -641,7 +641,12 @@ static int seek_to_next_config(struct libusb_context *ctx, - uint8_t *buffer, size_t len) - { - struct usbi_descriptor_header *header; -- int offset = 0; -+ int offset; -+ -+ /* Start seeking past the config descriptor */ -+ offset = LIBUSB_DT_CONFIG_SIZE; -+ buffer += LIBUSB_DT_CONFIG_SIZE; -+ len -= LIBUSB_DT_CONFIG_SIZE; - - while (len > 0) { - if (len < 2) { -@@ -718,7 +723,7 @@ static int parse_config_descriptors(struct libusb_device *dev) - } - - if (priv->sysfs_dir) { -- /* -+ /* - * In sysfs wTotalLength is ignored, instead the kernel returns a - * config descriptor with verified bLength fields, with descriptors - * with an invalid bLength removed. -@@ -727,8 +732,7 @@ static int parse_config_descriptors(struct libusb_device *dev) - int offset; - - if (num_configs > 1 && idx < num_configs - 1) { -- offset = seek_to_next_config(ctx, buffer + LIBUSB_DT_CONFIG_SIZE, -- remaining - LIBUSB_DT_CONFIG_SIZE); -+ offset = seek_to_next_config(ctx, buffer, remaining); - if (offset < 0) - return offset; - sysfs_config_len = (uint16_t)offset; --- -2.29.2 - diff --git a/0002-linux_usbfs-Gracefully-handle-buggy-devices-with-a-c.patch b/0002-linux_usbfs-Gracefully-handle-buggy-devices-with-a-c.patch deleted file mode 100644 index b95a501..0000000 --- a/0002-linux_usbfs-Gracefully-handle-buggy-devices-with-a-c.patch +++ /dev/null @@ -1,220 +0,0 @@ -From f38f09da98acc63966b65b72029b1f7f81166bef Mon Sep 17 00:00:00 2001 -From: Chris Dickens -Date: Mon, 8 Feb 2021 11:56:13 -0800 -Subject: [PATCH 2/2] linux_usbfs: Gracefully handle buggy devices with a - configuration 0 - -The USB spec states that a configuration value of 0 is reserved and is -used to indicate the device in not configured (e.g. is in the address -state). Unfortunately some devices do exist that violate this and use 0 -as the bConfigurationValue of the configuration descriptor. - -Improve how the Linux backend handles such non-conformant devices by -adding special handling around the configuration value 0. Most devices -will not require this special handling, but for those that do there is -no way to distinguish between the device being unconfigured and using -configuration 0. - -Closes #850 - -Signed-off-by: Chris Dickens ---- - libusb/os/linux_usbfs.c | 94 ++++++++++++++++++++++++++--------------- - libusb/version_nano.h | 2 +- - 2 files changed, 60 insertions(+), 36 deletions(-) - -diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c -index ebf8cfe..3a1894c 100644 ---- a/libusb/os/linux_usbfs.c -+++ b/libusb/os/linux_usbfs.c -@@ -128,7 +128,7 @@ struct linux_device_priv { - void *descriptors; - size_t descriptors_len; - struct config_descriptor *config_descriptors; -- uint8_t active_config; /* cache val for !sysfs_available */ -+ int active_config; /* cache val for !sysfs_available */ - }; - - struct linux_device_handle_priv { -@@ -169,6 +169,21 @@ struct linux_transfer_priv { - int iso_packet_offset; - }; - -+static int dev_has_config0(struct libusb_device *dev) -+{ -+ struct linux_device_priv *priv = usbi_get_device_priv(dev); -+ struct config_descriptor *config; -+ uint8_t idx; -+ -+ for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) { -+ config = &priv->config_descriptors[idx]; -+ if (config->desc->bConfigurationValue == 0) -+ return 1; -+ } -+ -+ return 0; -+} -+ - static int get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent) - { - struct libusb_context *ctx = DEVICE_CTX(dev); -@@ -574,22 +589,12 @@ static int sysfs_scan_device(struct libusb_context *ctx, const char *devname) - } - - /* read the bConfigurationValue for a device */ --static int sysfs_get_active_config(struct libusb_device *dev, uint8_t *config) -+static int sysfs_get_active_config(struct libusb_device *dev, int *config) - { - struct linux_device_priv *priv = usbi_get_device_priv(dev); -- int ret, tmp; -- -- ret = read_sysfs_attr(DEVICE_CTX(dev), priv->sysfs_dir, "bConfigurationValue", -- UINT8_MAX, &tmp); -- if (ret < 0) -- return ret; - -- if (tmp == -1) -- tmp = 0; /* unconfigured */ -- -- *config = (uint8_t)tmp; -- -- return 0; -+ return read_sysfs_attr(DEVICE_CTX(dev), priv->sysfs_dir, "bConfigurationValue", -+ UINT8_MAX, config); - } - - int linux_get_device_address(struct libusb_context *ctx, int detached, -@@ -765,6 +770,9 @@ static int parse_config_descriptors(struct libusb_device *dev) - } - } - -+ if (config_desc->bConfigurationValue == 0) -+ usbi_warn(ctx, "device has configuration 0"); -+ - priv->config_descriptors[idx].desc = config_desc; - priv->config_descriptors[idx].actual_len = config_len; - -@@ -798,7 +806,7 @@ static int op_get_active_config_descriptor(struct libusb_device *dev, - { - struct linux_device_priv *priv = usbi_get_device_priv(dev); - void *config_desc; -- uint8_t active_config; -+ int active_config; - int r; - - if (priv->sysfs_dir) { -@@ -810,12 +818,12 @@ static int op_get_active_config_descriptor(struct libusb_device *dev, - active_config = priv->active_config; - } - -- if (active_config == 0) { -+ if (active_config == -1) { - usbi_err(DEVICE_CTX(dev), "device unconfigured"); - return LIBUSB_ERROR_NOT_FOUND; - } - -- r = op_get_config_descriptor_by_value(dev, active_config, &config_desc); -+ r = op_get_config_descriptor_by_value(dev, (uint8_t)active_config, &config_desc); - if (r < 0) - return r; - -@@ -863,17 +871,26 @@ static int usbfs_get_active_config(struct libusb_device *dev, int fd) - - /* we hit this error path frequently with buggy devices :( */ - usbi_warn(DEVICE_CTX(dev), "get configuration failed, errno=%d", errno); -+ -+ /* assume the current configuration is the first one if we have -+ * the configuration descriptors, otherwise treat the device -+ * as unconfigured. */ -+ if (priv->config_descriptors) -+ priv->active_config = (int)priv->config_descriptors[0].desc->bConfigurationValue; -+ else -+ priv->active_config = -1; - } else if (active_config == 0) { -- /* some buggy devices have a configuration 0, but we're -- * reaching into the corner of a corner case here, so let's -- * not support buggy devices in these circumstances. -- * stick to the specs: a configuration value of 0 means -- * unconfigured. */ -- usbi_warn(DEVICE_CTX(dev), "active cfg 0? assuming unconfigured device"); -+ if (dev_has_config0(dev)) { -+ /* some buggy devices have a configuration 0, but we're -+ * reaching into the corner of a corner case here. */ -+ priv->active_config = 0; -+ } else { -+ priv->active_config = -1; -+ } -+ } else { -+ priv->active_config = (int)active_config; - } - -- priv->active_config = active_config; -- - return LIBUSB_SUCCESS; - } - -@@ -1004,9 +1021,9 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum, - usbi_warn(ctx, "Missing rw usbfs access; cannot determine " - "active configuration descriptor"); - if (priv->config_descriptors) -- priv->active_config = priv->config_descriptors[0].desc->bConfigurationValue; -+ priv->active_config = (int)priv->config_descriptors[0].desc->bConfigurationValue; - else -- priv->active_config = 0; /* No config dt */ -+ priv->active_config = -1; /* No config dt */ - - return LIBUSB_SUCCESS; - } -@@ -1428,22 +1445,27 @@ static int op_get_configuration(struct libusb_device_handle *handle, - uint8_t *config) - { - struct linux_device_priv *priv = usbi_get_device_priv(handle->dev); -+ int active_config; - int r; - - if (priv->sysfs_dir) { -- r = sysfs_get_active_config(handle->dev, config); -+ r = sysfs_get_active_config(handle->dev, &active_config); - } else { - struct linux_device_handle_priv *hpriv = usbi_get_device_handle_priv(handle); - - r = usbfs_get_active_config(handle->dev, hpriv->fd); - if (r == LIBUSB_SUCCESS) -- *config = priv->active_config; -+ active_config = priv->active_config; - } - if (r < 0) - return r; - -- if (*config == 0) -- usbi_err(HANDLE_CTX(handle), "device unconfigured"); -+ if (active_config == -1) { -+ usbi_warn(HANDLE_CTX(handle), "device unconfigured"); -+ active_config = 0; -+ } -+ -+ *config = (uint8_t)active_config; - - return 0; - } -@@ -1467,11 +1489,13 @@ static int op_set_configuration(struct libusb_device_handle *handle, int config) - return LIBUSB_ERROR_OTHER; - } - -- if (config == -1) -- config = 0; -+ /* if necessary, update our cached active config descriptor */ -+ if (!priv->sysfs_dir) { -+ if (config == 0 && !dev_has_config0(handle->dev)) -+ config = -1; - -- /* update our cached active config descriptor */ -- priv->active_config = (uint8_t)config; -+ priv->active_config = config; -+ } - - return LIBUSB_SUCCESS; - } --- -2.29.2 - diff --git a/libusb1.spec b/libusb1.spec index 4ec2a3d..8eb7ef5 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -1,6 +1,6 @@ Summary: Library for accessing USB devices Name: libusb1 -Version: 1.0.24 +Version: 1.0.25 Release: %autorelease Source0: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2 License: LGPLv2+ @@ -12,9 +12,6 @@ BuildRequires: gcc Provides: libusbx = %{version}-%{release} Obsoletes: libusbx < %{version}-%{release} -Patch001: 0001-linux_usbfs-Accept-sysfs-attributes-not-terminated-w.patch -Patch002: 0001-linux_usbfs-Fix-parsing-of-descriptors-for-multi-con.patch -Patch003: 0002-linux_usbfs-Gracefully-handle-buggy-devices-with-a-c.patch %description This package provides a way for applications to access USB devices. diff --git a/sources b/sources index 9d92dff..75982ca 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (libusb-1.0.24.tar.bz2) = 5aea36a530aaa15c6dd656d0ed3ce204522c9946d8d39ffbb290dab4a98cda388a2598da4995123d1032324056090bd429e702459626d3e8d7daeebc4e7ff3dc +SHA512 (libusb-1.0.25.tar.bz2) = f1e6e5577d4bd1ff136927dc66c615014a06ac332ddd797b1d1ad5f7b68e2405e66068dcb210e2f0ae3e31681603ef72efbd88bf7fbe0eb41ce700fdc3f92f9d From 6719df21e171b22897c5dfbf24e41e6f7ae75f54 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 2 Feb 2022 20:08:21 +0100 Subject: [PATCH 05/42] Fix automatic changelog The changelog for the 1.0.25 update was missing as I rolled the move to automatic generation and the update into one commit. --- changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog b/changelog index 811f64d..5df34fe 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,6 @@ +* Wed Feb 02 2022 Benjamin Berg 1.0.25-1 +- Update to 1.0.25 + * Thu Jan 20 2022 Fedora Release Engineering - 1.0.24-5 - Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild From ac977fad6ed5042fb7c563ba8cc4effe5de184fa Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 10 Feb 2022 12:06:41 +0100 Subject: [PATCH 06/42] Fix a crash after libusb_exit API has been misused Resolves: #2050638 --- 1058.patch | 24 ++++++++++++++++++++++++ libusb1.spec | 3 +++ 2 files changed, 27 insertions(+) create mode 100644 1058.patch diff --git a/1058.patch b/1058.patch new file mode 100644 index 0000000..d504455 --- /dev/null +++ b/1058.patch @@ -0,0 +1,24 @@ +From 2529a3fc4f987f93e0774af865ac7cb6557bd0c2 Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Fri, 4 Feb 2022 22:50:28 +0100 +Subject: [PATCH] core: Unset device ctx if it has been destroyed + +Devices can outlive their context in some cases (in particular with +python garbage collection). Guard against this happening by clearing the +ctx pointer so that it is not pointing to uninitialized memory. +--- + libusb/core.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/libusb/core.c b/libusb/core.c +index 7893ac23..1c1ada14 100644 +--- a/libusb/core.c ++++ b/libusb/core.c +@@ -2441,6 +2441,7 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) + for_each_device(_ctx, dev) { + usbi_warn(_ctx, "device %d.%d still referenced", + dev->bus_number, dev->device_address); ++ DEVICE_CTX(dev) = NULL; + } + + if (!list_empty(&_ctx->open_devs)) diff --git a/libusb1.spec b/libusb1.spec index 8eb7ef5..34310dc 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -12,6 +12,9 @@ BuildRequires: gcc Provides: libusbx = %{version}-%{release} Obsoletes: libusbx < %{version}-%{release} +# Fix a crash after libusb_exit API has been misused +# https://bugzilla.redhat.com/show_bug.cgi?id=2050638 +Patch0001: https://github.com/libusb/libusb/pull/1058.patch %description This package provides a way for applications to access USB devices. From 4acb6872326fc4854dcbf749b8da1a31e7c0ab9d Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 10 Feb 2022 12:06:41 +0100 Subject: [PATCH 07/42] Fix a crash after libusb_exit API has been misused Resolves: #2050638 --- 1058.patch | 24 ++++++++++++++++++++++++ libusb1.spec | 3 +++ 2 files changed, 27 insertions(+) create mode 100644 1058.patch diff --git a/1058.patch b/1058.patch new file mode 100644 index 0000000..d504455 --- /dev/null +++ b/1058.patch @@ -0,0 +1,24 @@ +From 2529a3fc4f987f93e0774af865ac7cb6557bd0c2 Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Fri, 4 Feb 2022 22:50:28 +0100 +Subject: [PATCH] core: Unset device ctx if it has been destroyed + +Devices can outlive their context in some cases (in particular with +python garbage collection). Guard against this happening by clearing the +ctx pointer so that it is not pointing to uninitialized memory. +--- + libusb/core.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/libusb/core.c b/libusb/core.c +index 7893ac23..1c1ada14 100644 +--- a/libusb/core.c ++++ b/libusb/core.c +@@ -2441,6 +2441,7 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) + for_each_device(_ctx, dev) { + usbi_warn(_ctx, "device %d.%d still referenced", + dev->bus_number, dev->device_address); ++ DEVICE_CTX(dev) = NULL; + } + + if (!list_empty(&_ctx->open_devs)) diff --git a/libusb1.spec b/libusb1.spec index 8eb7ef5..34310dc 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -12,6 +12,9 @@ BuildRequires: gcc Provides: libusbx = %{version}-%{release} Obsoletes: libusbx < %{version}-%{release} +# Fix a crash after libusb_exit API has been misused +# https://bugzilla.redhat.com/show_bug.cgi?id=2050638 +Patch0001: https://github.com/libusb/libusb/pull/1058.patch %description This package provides a way for applications to access USB devices. From 2084da7d5a6d0c198d30e2e86e96cdf77f214730 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 15 Feb 2022 12:02:17 +0100 Subject: [PATCH 08/42] Fix a crash if a transfer outlives closing the device --- 1073.patch | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ libusb1.spec | 2 ++ 2 files changed, 74 insertions(+) create mode 100644 1073.patch diff --git a/1073.patch b/1073.patch new file mode 100644 index 0000000..8b91a48 --- /dev/null +++ b/1073.patch @@ -0,0 +1,72 @@ +From bf833ee6adf58bd4a4a468aa729cdc78bdc13ede Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Tue, 15 Feb 2022 11:13:41 +0100 +Subject: [PATCH 1/2] core: Catch NULL dev_handle when getting a transfer's + context + +The dev_handle will be set to NULL when the transfer is still in-flight +while the device is closed. In that case, the transfer free function +will try to access the context and would run into a NULL pointer +dereference. + +Add a test for dev_handle being valid before dereferencing it further. +--- + libusb/libusbi.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libusb/libusbi.h b/libusb/libusbi.h +index 158a9af5..dde43df2 100644 +--- a/libusb/libusbi.h ++++ b/libusb/libusbi.h +@@ -330,7 +330,7 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, + + #define DEVICE_CTX(dev) ((dev)->ctx) + #define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) +-#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) ++#define TRANSFER_CTX(transfer) ((transfer)->dev_handle ? HANDLE_CTX((transfer)->dev_handle) : NULL) + #define ITRANSFER_CTX(itransfer) \ + (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer))) + + +From 6428090ea77dfb80906a146977ea7fd6de4718c8 Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Tue, 15 Feb 2022 10:59:00 +0100 +Subject: [PATCH 2/2] io: Unset dev_handle when removing transfer from flying + list + +API users might hold on to transfers a bit longer than they are in the +flying list. If they then close the device prior to freeing all +transfers, we would end up with invalid pointers to the device. + +Fix this by setting the device handle to NULL when removing the device +from the flying list. This matches the behaviour when the device is +closed while the transfer is still in the flying list. + +Specifically, the libgusb wrapper will currently only free the +underlying transfer in a later mainloop iteration (as a side effect on +how GTask does memory management). It is possible to fix this, but it +would make memory management within libgusb much more error prone. +--- + libusb/io.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/libusb/io.c b/libusb/io.c +index 0d2ac9ea..4e6d8984 100644 +--- a/libusb/io.c ++++ b/libusb/io.c +@@ -1456,6 +1456,7 @@ static int add_to_flying_list(struct usbi_transfer *itransfer) + * if it fails to update the timer for the next timeout. */ + static int remove_from_flying_list(struct usbi_transfer *itransfer) + { ++ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); + int rearm_timer; + int r = 0; +@@ -1466,6 +1467,7 @@ static int remove_from_flying_list(struct usbi_transfer *itransfer) + list_del(&itransfer->list); + if (rearm_timer) + r = arm_timer_for_next_timeout(ctx); ++ transfer->dev_handle = NULL; + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + return r; diff --git a/libusb1.spec b/libusb1.spec index 34310dc..901fbd2 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -15,6 +15,8 @@ Obsoletes: libusbx < %{version}-%{release} # Fix a crash after libusb_exit API has been misused # https://bugzilla.redhat.com/show_bug.cgi?id=2050638 Patch0001: https://github.com/libusb/libusb/pull/1058.patch +# Fix a crash if a transfer outlives closing the device +Patch0002: https://github.com/libusb/libusb/pull/1073.patch %description This package provides a way for applications to access USB devices. From ddf6170c929636e5ab3098cde57ace68fa7cd920 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 15 Feb 2022 12:02:17 +0100 Subject: [PATCH 09/42] Fix a crash if a transfer outlives closing the device --- 1073.patch | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ libusb1.spec | 2 ++ 2 files changed, 74 insertions(+) create mode 100644 1073.patch diff --git a/1073.patch b/1073.patch new file mode 100644 index 0000000..8b91a48 --- /dev/null +++ b/1073.patch @@ -0,0 +1,72 @@ +From bf833ee6adf58bd4a4a468aa729cdc78bdc13ede Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Tue, 15 Feb 2022 11:13:41 +0100 +Subject: [PATCH 1/2] core: Catch NULL dev_handle when getting a transfer's + context + +The dev_handle will be set to NULL when the transfer is still in-flight +while the device is closed. In that case, the transfer free function +will try to access the context and would run into a NULL pointer +dereference. + +Add a test for dev_handle being valid before dereferencing it further. +--- + libusb/libusbi.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libusb/libusbi.h b/libusb/libusbi.h +index 158a9af5..dde43df2 100644 +--- a/libusb/libusbi.h ++++ b/libusb/libusbi.h +@@ -330,7 +330,7 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, + + #define DEVICE_CTX(dev) ((dev)->ctx) + #define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) +-#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) ++#define TRANSFER_CTX(transfer) ((transfer)->dev_handle ? HANDLE_CTX((transfer)->dev_handle) : NULL) + #define ITRANSFER_CTX(itransfer) \ + (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer))) + + +From 6428090ea77dfb80906a146977ea7fd6de4718c8 Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Tue, 15 Feb 2022 10:59:00 +0100 +Subject: [PATCH 2/2] io: Unset dev_handle when removing transfer from flying + list + +API users might hold on to transfers a bit longer than they are in the +flying list. If they then close the device prior to freeing all +transfers, we would end up with invalid pointers to the device. + +Fix this by setting the device handle to NULL when removing the device +from the flying list. This matches the behaviour when the device is +closed while the transfer is still in the flying list. + +Specifically, the libgusb wrapper will currently only free the +underlying transfer in a later mainloop iteration (as a side effect on +how GTask does memory management). It is possible to fix this, but it +would make memory management within libgusb much more error prone. +--- + libusb/io.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/libusb/io.c b/libusb/io.c +index 0d2ac9ea..4e6d8984 100644 +--- a/libusb/io.c ++++ b/libusb/io.c +@@ -1456,6 +1456,7 @@ static int add_to_flying_list(struct usbi_transfer *itransfer) + * if it fails to update the timer for the next timeout. */ + static int remove_from_flying_list(struct usbi_transfer *itransfer) + { ++ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); + int rearm_timer; + int r = 0; +@@ -1466,6 +1467,7 @@ static int remove_from_flying_list(struct usbi_transfer *itransfer) + list_del(&itransfer->list); + if (rearm_timer) + r = arm_timer_for_next_timeout(ctx); ++ transfer->dev_handle = NULL; + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + return r; diff --git a/libusb1.spec b/libusb1.spec index 34310dc..901fbd2 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -15,6 +15,8 @@ Obsoletes: libusbx < %{version}-%{release} # Fix a crash after libusb_exit API has been misused # https://bugzilla.redhat.com/show_bug.cgi?id=2050638 Patch0001: https://github.com/libusb/libusb/pull/1058.patch +# Fix a crash if a transfer outlives closing the device +Patch0002: https://github.com/libusb/libusb/pull/1073.patch %description This package provides a way for applications to access USB devices. From 07d18ab2e1c34fee14c1fbcef456dc9e06772d83 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 22 Feb 2022 12:02:16 +0100 Subject: [PATCH 10/42] Update regression fix patch for late transfer free's The previous patch broke the synchronous API, pull in the new version of the proposed upstream fix. --- 1073.patch | 183 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 122 insertions(+), 61 deletions(-) diff --git a/1073.patch b/1073.patch index 8b91a48..7ecce5b 100644 --- a/1073.patch +++ b/1073.patch @@ -1,72 +1,133 @@ -From bf833ee6adf58bd4a4a468aa729cdc78bdc13ede Mon Sep 17 00:00:00 2001 +From 1cb11c5ba0d1d1266fe4ddd70f29d081f9d16802 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 15 Feb 2022 11:13:41 +0100 -Subject: [PATCH 1/2] core: Catch NULL dev_handle when getting a transfer's - context +Subject: [PATCH] io: Track device in usbi_transfer -The dev_handle will be set to NULL when the transfer is still in-flight -while the device is closed. In that case, the transfer free function -will try to access the context and would run into a NULL pointer -dereference. +transfer->dev_handle currently has the behaviour that it will be unset +if the device is closed. The sync API uses this fact to catch an error +case. -Add a test for dev_handle being valid before dereferencing it further. +In other cases, transfer->dev_handle will keep its value, which means +that if the transfer lives longer than the device handle, the pointer +becomes invalid. + +The transfer does however keep a reference to the device, which owns the +pointer to the context. As such, we can track this reference internal to +the transfer, and it is set while the transfer is in-flight. + +With this, switch the logging infrastructure to use itransfer->dev->ctx +while checking that itransfer->dev is non-NULL. + +Note that this was a regression caused by 6cae9c6dbd74 ("core: update +usbi_dbg to take the context as an argument"), specifically when +resolving the context while freeing a transfer after closing a device. + +Note that the transfer will now keep a reference to the device until it +is free'ed. This allows it to use the correct context for logging even +in libusb_free_transfer. + +The alternative to all this would be to just explicitly pass NULL to the +log handler in libusb_free_transfer. --- - libusb/libusbi.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/libusb/libusbi.h b/libusb/libusbi.h -index 158a9af5..dde43df2 100644 ---- a/libusb/libusbi.h -+++ b/libusb/libusbi.h -@@ -330,7 +330,7 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, - - #define DEVICE_CTX(dev) ((dev)->ctx) - #define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) --#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) -+#define TRANSFER_CTX(transfer) ((transfer)->dev_handle ? HANDLE_CTX((transfer)->dev_handle) : NULL) - #define ITRANSFER_CTX(itransfer) \ - (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer))) - - -From 6428090ea77dfb80906a146977ea7fd6de4718c8 Mon Sep 17 00:00:00 2001 -From: Benjamin Berg -Date: Tue, 15 Feb 2022 10:59:00 +0100 -Subject: [PATCH 2/2] io: Unset dev_handle when removing transfer from flying - list - -API users might hold on to transfers a bit longer than they are in the -flying list. If they then close the device prior to freeing all -transfers, we would end up with invalid pointers to the device. - -Fix this by setting the device handle to NULL when removing the device -from the flying list. This matches the behaviour when the device is -closed while the transfer is still in the flying list. - -Specifically, the libgusb wrapper will currently only free the -underlying transfer in a later mainloop iteration (as a side effect on -how GTask does memory management). It is possible to fix this, but it -would make memory management within libgusb much more error prone. ---- - libusb/io.c | 2 ++ - 1 file changed, 2 insertions(+) + libusb/io.c | 20 ++++++++++++-------- + libusb/libusbi.h | 10 +++++++--- + 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/libusb/io.c b/libusb/io.c -index 0d2ac9ea..4e6d8984 100644 +index 0d2ac9ea..b919e9d9 100644 --- a/libusb/io.c +++ b/libusb/io.c -@@ -1456,6 +1456,7 @@ static int add_to_flying_list(struct usbi_transfer *itransfer) - * if it fails to update the timer for the next timeout. */ - static int remove_from_flying_list(struct usbi_transfer *itransfer) - { -+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - struct libusb_context *ctx = ITRANSFER_CTX(itransfer); - int rearm_timer; - int r = 0; -@@ -1466,6 +1467,7 @@ static int remove_from_flying_list(struct usbi_transfer *itransfer) - list_del(&itransfer->list); - if (rearm_timer) - r = arm_timer_for_next_timeout(ctx); -+ transfer->dev_handle = NULL; - usbi_mutex_unlock(&ctx->flying_transfers_lock); +@@ -1344,6 +1344,8 @@ void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer) + itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + usbi_mutex_destroy(&itransfer->lock); ++ if (itransfer->dev) ++ libusb_unref_device(itransfer->dev); + + priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); + ptr = (unsigned char *)itransfer - priv_size; +@@ -1489,9 +1491,15 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) + { + struct usbi_transfer *itransfer = + LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); +- struct libusb_context *ctx = TRANSFER_CTX(transfer); ++ struct libusb_context *ctx; + int r; + ++ assert(transfer->dev_handle); ++ if (itransfer->dev) ++ libusb_unref_device(itransfer->dev); ++ itransfer->dev = libusb_ref_device(transfer->dev_handle->dev); ++ ++ ctx = HANDLE_CTX(transfer->dev_handle); + usbi_dbg(ctx, "transfer %p", transfer); + + /* +@@ -1551,8 +1559,6 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) + r = usbi_backend.submit_transfer(itransfer); + if (r == LIBUSB_SUCCESS) { + itransfer->state_flags |= USBI_TRANSFER_IN_FLIGHT; +- /* keep a reference to this device */ +- libusb_ref_device(transfer->dev_handle->dev); + } + usbi_mutex_unlock(&itransfer->lock); + +@@ -1659,7 +1665,6 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + { + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); +- struct libusb_device_handle *dev_handle = transfer->dev_handle; + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); + uint8_t flags; + int r; +@@ -1693,7 +1698,6 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + * this point. */ + if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) + libusb_free_transfer(transfer); +- libusb_unref_device(dev_handle->dev); return r; + } + +@@ -1727,10 +1731,10 @@ int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer) + * function will be called the next time an event handler runs. */ + void usbi_signal_transfer_completion(struct usbi_transfer *itransfer) + { +- libusb_device_handle *dev_handle = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->dev_handle; ++ struct libusb_device *dev = itransfer->dev; + +- if (dev_handle) { +- struct libusb_context *ctx = HANDLE_CTX(dev_handle); ++ if (dev) { ++ struct libusb_context *ctx = DEVICE_CTX(dev); + unsigned int event_flags; + + usbi_mutex_lock(&ctx->event_data_lock); +diff --git a/libusb/libusbi.h b/libusb/libusbi.h +index 158a9af5..5f0d5c2e 100644 +--- a/libusb/libusbi.h ++++ b/libusb/libusbi.h +@@ -329,10 +329,11 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, + #endif /* ENABLE_LOGGING */ + + #define DEVICE_CTX(dev) ((dev)->ctx) +-#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) +-#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) ++#define HANDLE_CTX(handle) ((handle) ? DEVICE_CTX((handle)->dev) : NULL) + #define ITRANSFER_CTX(itransfer) \ +- (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer))) ++ ((itransfer)->dev ? DEVICE_CTX((itransfer)->dev) : NULL) ++#define TRANSFER_CTX(transfer) \ ++ (ITRANSFER_CTX(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer))) + + #define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN)) + #define IS_EPOUT(ep) (!IS_EPIN(ep)) +@@ -562,6 +563,9 @@ struct usbi_transfer { + uint32_t state_flags; /* Protected by usbi_transfer->lock */ + uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */ + ++ /* This is used for logging mostly. As long as it is set, the */ ++ struct libusb_device *dev; ++ + /* this lock is held during libusb_submit_transfer() and + * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate + * cancellation, submission-during-cancellation, etc). the OS backend From 11a16ae2a858b3c507baa6e13ccecfffb646bcdb Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 22 Feb 2022 12:05:43 +0100 Subject: [PATCH 11/42] Add umockdev based tests See https://github.com/libusb/libusb/pull/1078 --- ...-tests-Add-some-umockdev-based-tests.patch | 1036 +++++++++++++++++ libusb1.spec | 6 + 2 files changed, 1042 insertions(+) create mode 100644 0001-tests-Add-some-umockdev-based-tests.patch diff --git a/0001-tests-Add-some-umockdev-based-tests.patch b/0001-tests-Add-some-umockdev-based-tests.patch new file mode 100644 index 0000000..204879f --- /dev/null +++ b/0001-tests-Add-some-umockdev-based-tests.patch @@ -0,0 +1,1036 @@ +From 4f413d618311edf422b1b8c7ddb05919517f151d Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Fri, 18 Feb 2022 16:26:29 +0100 +Subject: [PATCH 1/3] tests: Add some umockdev based tests + +Add some basic umockdev tests. The setup in this case is all in one +process, but umockdev in principle could also work between processes. + +This is just a start, more tests would make sense. +--- + configure.ac | 7 + + tests/Makefile.am | 11 +- + tests/umockdev.c | 965 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 982 insertions(+), 1 deletion(-) + create mode 100644 tests/umockdev.c + +diff --git a/configure.ac b/configure.ac +index f6cf2f9..5336463 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -177,6 +177,12 @@ linux) + dnl system has udev. use it or fail! + AC_CHECK_HEADER([libudev.h], [], [AC_MSG_ERROR([udev support requested but libudev header not installed])]) + AC_CHECK_LIB([udev], [udev_new], [], [AC_MSG_ERROR([udev support requested but libudev not installed])]) ++ ++ # We can build umockdev tests (if available) ++ PKG_PROG_PKG_CONFIG ++ PKG_CHECK_MODULES(UMOCKDEV, umockdev-1.0 >= 0.16.0, ac_have_umockdev=yes, ac_have_umockdev=no) ++ AC_SUBST(UMOCKDEV_CFLAGS) ++ AC_SUBST(UMOCKDEV_LIBS) + else + AC_CHECK_HEADERS([asm/types.h]) + AC_CHECK_HEADER([linux/netlink.h], [], [AC_MSG_ERROR([Linux netlink header not found])]) +@@ -356,6 +362,7 @@ AC_ARG_ENABLE([tests-build], + + AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" != xno]) + AM_CONDITIONAL([BUILD_TESTS], [test "x$build_tests" != xno]) ++AM_CONDITIONAL([BUILD_UMOCKDEV_TEST], [test "x$ac_have_umockdev" = xyes -a "x$log_enabled" != xno]) + AM_CONDITIONAL([CREATE_IMPORT_LIB], [test "x$create_import_lib" = xyes]) + AM_CONDITIONAL([OS_DARWIN], [test "x$backend" = xdarwin]) + AM_CONDITIONAL([OS_HAIKU], [test "x$backend" = xhaiku]) +diff --git a/tests/Makefile.am b/tests/Makefile.am +index cb8fad9..ace44bf 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -2,6 +2,15 @@ AM_CPPFLAGS = -I$(top_srcdir)/libusb + LDADD = ../libusb/libusb-1.0.la + LIBS = + ++stress_SOURCES = stress.c libusb_testlib.h testlib.c ++ + noinst_PROGRAMS = stress + +-stress_SOURCES = stress.c libusb_testlib.h testlib.c ++if BUILD_UMOCKDEV_TEST ++# NOTE: We add libumockdev-preload.so so that we can run tests in-process ++umockdev_CPPFLAGS = ${UMOCKDEV_CFLAGS} -I$(top_srcdir)/libusb ++umockdev_LDFLAGS = -Wl,--no-as-needed -lumockdev-preload ${UMOCKDEV_LIBS} ++umockdev_SOURCES = umockdev.c ++ ++noinst_PROGRAMS += umockdev ++endif +diff --git a/tests/umockdev.c b/tests/umockdev.c +new file mode 100644 +index 0000000..b2af512 +--- /dev/null ++++ b/tests/umockdev.c +@@ -0,0 +1,965 @@ ++/* ++ * libusb umockdev based tests ++ * ++ * Copyright (C) 2022 Benjamin Berg ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program; If not, see . ++ */ ++ ++#define _GNU_SOURCE ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "libusb.h" ++ ++#include "umockdev.h" ++ ++#define UNUSED_DATA __attribute__ ((unused)) gconstpointer unused_data ++ ++/* avoid leak reports inside assertions; leaking stuff on assertion failures does not matter in tests */ ++#if !defined(__clang__) ++#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak" ++#pragma GCC diagnostic ignored "-Wanalyzer-file-leak" ++#endif ++ ++typedef struct { ++ pid_t thread; ++ libusb_context *ctx; ++ enum libusb_log_level level; ++ char *str; ++} LogMessage; ++ ++static void ++log_message_free(LogMessage *msg) ++{ ++ g_free(msg->str); ++ g_free(msg); ++} ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(LogMessage, log_message_free) ++ ++typedef struct _UsbChat UsbChat; ++ ++struct _UsbChat { ++ gboolean submit; ++ gboolean reap; ++ UsbChat *reaps; ++ UsbChat *next; ++ ++ /* struct usbdevfs_urb */ ++ unsigned char type; ++ unsigned char endpoint; ++ int status; ++ unsigned int flags; ++ const unsigned char *buffer; ++ int buffer_length; ++ int actual_length; ++ ++ /* */ ++ UMockdevIoctlData *submit_urb; ++}; ++ ++typedef struct { ++ UMockdevTestbed *testbed; ++ UMockdevIoctlBase *handler; ++ struct libusb_context *ctx; ++ ++ gchar *root_dir; ++ gchar *sys_dir; ++ ++ gboolean libusb_log_silence; ++ GList *libusb_log; ++ ++ UsbChat *chat; ++ GList *flying_urbs; ++ GList *discarded_urbs; ++ ++ /* GMutex confuses tsan unecessarily */ ++ pthread_mutex_t mutex; ++} UMockdevTestbedFixture; ++ ++/* Global for log handler */ ++static UMockdevTestbedFixture *cur_fixture = NULL; ++ ++static void ++log_handler(libusb_context *ctx, enum libusb_log_level level, const char *str) ++{ ++ /* May be called from different threads without synchronization! */ ++ LogMessage *msg; ++ pid_t tid = gettid(); ++ ++ g_assert (cur_fixture != NULL); ++ g_assert(pthread_mutex_lock(&cur_fixture->mutex) == 0); ++ ++ msg = g_new0(LogMessage, 1); ++ msg->ctx = ctx; ++ msg->level = level; ++ msg->str = g_strchomp (g_strdup(str)); ++ msg->thread = tid; ++ ++ if (!cur_fixture->libusb_log_silence) ++ g_printerr("%s\n", msg->str); ++ ++ cur_fixture->libusb_log = g_list_append(cur_fixture->libusb_log, msg); ++ pthread_mutex_unlock(&cur_fixture->mutex); ++} ++ ++static void ++log_handler_null(libusb_context *ctx, enum libusb_log_level level, const char *str) ++{ ++ (void) ctx; ++ (void) level; ++ (void) str; ++} ++ ++static void ++clear_libusb_log(UMockdevTestbedFixture * fixture, enum libusb_log_level level) ++{ ++ g_assert(pthread_mutex_lock(&fixture->mutex) == 0); ++ ++ while (fixture->libusb_log) { ++ LogMessage *msg = fixture->libusb_log->data; ++ ++ g_assert(msg->ctx == fixture->ctx); ++ ++ if (msg->level < level) { ++ pthread_mutex_unlock(&fixture->mutex); ++ return; ++ } ++ ++ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log); ++ log_message_free(msg); ++ } ++ pthread_mutex_unlock(&fixture->mutex); ++} ++ ++static void ++assert_libusb_log_msg(UMockdevTestbedFixture * fixture, enum libusb_log_level level, const char *re) ++{ ++ g_assert(pthread_mutex_lock(&fixture->mutex) == 0); ++ ++ while (fixture->libusb_log) { ++ g_autoptr(LogMessage) msg = NULL; ++ ++ if (fixture->libusb_log == NULL) ++ g_error ("No level %d message found searching for %s", level, re); ++ ++ msg = fixture->libusb_log->data; ++ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log); ++ ++ if (msg->ctx != fixture->ctx) ++ g_error ("Saw unexpected message \"%s\" from context %p while %p was expected", ++ msg->str, msg->ctx, fixture->ctx); ++ ++ if (msg->level == level && g_regex_match_simple(re, msg->str, 0, 0)) { ++ pthread_mutex_unlock(&fixture->mutex); ++ return; ++ } ++ ++ /* Allow skipping INFO and DEBUG messages */ ++ if (msg->level >= LIBUSB_LOG_LEVEL_INFO) ++ continue; ++ ++ g_error ("Searched for \"%s\" (%d) but found \"%s\" (%d)", re, level, msg->str, msg->level); ++ } ++ ++ pthread_mutex_unlock(&fixture->mutex); ++ g_error ("Searched for \"%s\" (%d) but no message matched", re, level); ++} ++ ++#if 0 ++static void ++assert_libusb_no_log_msg(UMockdevTestbedFixture * fixture, enum libusb_log_level level, const char *re) ++{ ++ g_assert(pthread_mutex_lock(&fixture->mutex) == 0); ++ ++ while (fixture->libusb_log) { ++ g_autoptr(LogMessage) msg = NULL; ++ gboolean matching; ++ ++ msg = fixture->libusb_log->data; ++ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log); ++ ++ g_assert(msg->ctx == fixture->ctx); ++ ++ matching = (msg->level == level && g_regex_match_simple(re, msg->str, 0, 0)); ++ ++ /* Allow skipping INFO and DEBUG messages */ ++ if (!matching && msg->level >= LIBUSB_LOG_LEVEL_INFO) ++ continue; ++ ++ g_error ("Asserting \"%s\" (%d) not logged and found \"%s\" (%d)", re, level, msg->str, msg->level); ++ } ++ ++ pthread_mutex_unlock(&fixture->mutex); ++} ++#endif ++ ++static void ++dump_buffer(const unsigned char *buffer, int len) ++{ ++ g_autoptr(GString) line = NULL; ++ ++ line = g_string_new (""); ++ for (gint i = 0; i < len; i++) { ++ g_string_append_printf(line, "%02x ", buffer[i]); ++ if ((i + 1) % 16 == 0) { ++ g_printerr(" %s\n", line->str); ++ g_string_set_size(line, 0); ++ } ++ } ++ ++ if (line->len) ++ g_printerr(" %s\n", line->str); ++} ++ ++static gint ++cmp_ioctl_data_addr(const void *data, const void *addr) ++{ ++ return ((const UMockdevIoctlData*) data)->client_addr != (gulong) addr; ++} ++ ++static gboolean ++handle_ioctl_cb (UMockdevIoctlBase *handler, UMockdevIoctlClient *client, UMockdevTestbedFixture *fixture) ++{ ++ UMockdevIoctlData *ioctl_arg; ++ long int request; ++ struct usbdevfs_urb *urb; ++ ++ (void) handler; ++ ++ request = umockdev_ioctl_client_get_request (client); ++ ioctl_arg = umockdev_ioctl_client_get_arg (client); ++ ++ /* NOTE: We share the address space, dereferencing pointers *will* work. ++ * However, to make tsan work, we still stick to the API that resolves ++ * the data into a local copy! */ ++ ++ switch (request) { ++ case USBDEVFS_GET_CAPABILITIES: { ++ g_autoptr(UMockdevIoctlData) d = NULL; ++ d = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(guint32), NULL); ++ ++ *(guint32*) d->data = USBDEVFS_CAP_BULK_SCATTER_GATHER | ++ USBDEVFS_CAP_BULK_CONTINUATION | ++ USBDEVFS_CAP_NO_PACKET_SIZE_LIM | ++ USBDEVFS_CAP_REAP_AFTER_DISCONNECT | ++ USBDEVFS_CAP_ZERO_PACKET; ++ ++ umockdev_ioctl_client_complete(client, 0, 0); ++ return TRUE; ++ } ++ ++ case USBDEVFS_CLAIMINTERFACE: ++ case USBDEVFS_RELEASEINTERFACE: ++ case USBDEVFS_CLEAR_HALT: ++ case USBDEVFS_RESET: ++ case USBDEVFS_RESETEP: ++ umockdev_ioctl_client_complete(client, 0, 0); ++ return TRUE; ++ ++ case USBDEVFS_SUBMITURB: { ++ g_autoptr(UMockdevIoctlData) urb_buffer = NULL; ++ g_autoptr(UMockdevIoctlData) urb_data = NULL; ++ gsize buflen; ++ ++ if (!fixture->chat || !fixture->chat->submit) ++ return FALSE; ++ ++ buflen = fixture->chat->buffer_length; ++ if (fixture->chat->type == USBDEVFS_URB_TYPE_CONTROL) ++ buflen = 8; ++ ++ urb_data = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(struct usbdevfs_urb), NULL); ++ urb = (struct usbdevfs_urb*) urb_data->data; ++ urb_buffer = umockdev_ioctl_data_resolve(urb_data, G_STRUCT_OFFSET(struct usbdevfs_urb, buffer), urb->buffer_length, NULL); ++ ++ if (fixture->chat->type == urb->type && ++ fixture->chat->endpoint == urb->endpoint && ++ fixture->chat->buffer_length == urb->buffer_length && ++ (fixture->chat->buffer == NULL || memcmp (fixture->chat->buffer, urb_buffer->data, buflen) == 0)) { ++ fixture->flying_urbs = g_list_append (fixture->flying_urbs, umockdev_ioctl_data_ref(urb_data)); ++ ++ if (fixture->chat->reaps) ++ fixture->chat->reaps->submit_urb = urb_data; ++ ++ if (fixture->chat->status) ++ umockdev_ioctl_client_complete(client, -1, -fixture->chat->status); ++ else ++ umockdev_ioctl_client_complete(client, 0, 0); ++ ++ if (fixture->chat->next) ++ fixture->chat = fixture->chat->next; ++ else ++ fixture->chat += 1; ++ return TRUE; ++ } ++ ++ /* chat message didn't match, don't accept it */ ++ g_printerr("Could not process submit urb:\n"); ++ g_printerr(" t: %d, ep: %d, actual_length: %d, buffer_length: %d\n", ++ urb->type, urb->endpoint, urb->actual_length, urb->buffer_length); ++ if (urb->type == USBDEVFS_URB_TYPE_CONTROL || urb->endpoint & LIBUSB_ENDPOINT_IN) ++ dump_buffer(urb->buffer, urb->buffer_length); ++ g_printerr("Looking for:\n"); ++ g_printerr(" t: %d, ep: %d, actual_length: %d, buffer_length: %d\n", ++ fixture->chat->type, fixture->chat->endpoint, ++ fixture->chat->actual_length, fixture->chat->buffer_length); ++ if (fixture->chat->buffer) ++ dump_buffer(fixture->chat->buffer, buflen); ++ ++ return FALSE; ++ } ++ ++ case USBDEVFS_REAPURB: ++ case USBDEVFS_REAPURBNDELAY: { ++ g_autoptr(UMockdevIoctlData) urb_ptr = NULL; ++ g_autoptr(UMockdevIoctlData) urb_data = NULL; ++ ++ if (fixture->discarded_urbs) { ++ urb_data = fixture->discarded_urbs->data; ++ urb = (struct usbdevfs_urb*) urb_data->data; ++ fixture->discarded_urbs = g_list_delete_link(fixture->discarded_urbs, fixture->discarded_urbs); ++ urb->status = -ENOENT; ++ ++ urb_ptr = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(gpointer), NULL); ++ umockdev_ioctl_data_set_ptr(urb_ptr, 0, urb_data); ++ ++ umockdev_ioctl_client_complete(client, 0, 0); ++ return TRUE; ++ } ++ ++ if (fixture->chat && fixture->chat->reap) { ++ GList *l = g_list_find(fixture->flying_urbs, fixture->chat->submit_urb); ++ ++ if (l) { ++ fixture->flying_urbs = g_list_remove_link(fixture->flying_urbs, fixture->flying_urbs); ++ ++ urb_data = fixture->chat->submit_urb; ++ urb = (struct usbdevfs_urb*) urb_data->data; ++ urb->actual_length = fixture->chat->actual_length; ++ if (urb->type == USBDEVFS_URB_TYPE_CONTROL && urb->actual_length) ++ urb->actual_length -= 8; ++ if (fixture->chat->buffer) ++ memcpy(urb->buffer, fixture->chat->buffer, fixture->chat->actual_length); ++ urb->status = fixture->chat->status; ++ ++ urb_ptr = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(gpointer), NULL); ++ umockdev_ioctl_data_set_ptr(urb_ptr, 0, urb_data); ++ if (fixture->chat->next) ++ fixture->chat = fixture->chat->next; ++ else ++ fixture->chat += 1; ++ umockdev_ioctl_client_complete(client, 0, 0); ++ return TRUE; ++ } ++ } ++ ++ /* Nothing to reap */ ++ umockdev_ioctl_client_complete(client, -1, EAGAIN); ++ return TRUE; ++ } ++ ++ case USBDEVFS_DISCARDURB: { ++ GList *l = g_list_find_custom(fixture->flying_urbs, *(void**) ioctl_arg->data, cmp_ioctl_data_addr); ++ ++ if (l) { ++ fixture->discarded_urbs = g_list_append(fixture->discarded_urbs, l->data); ++ fixture->flying_urbs = g_list_delete_link(fixture->flying_urbs, l); ++ umockdev_ioctl_client_complete(client, 0, 0); ++ } else { ++ umockdev_ioctl_client_complete(client, -1, EINVAL); ++ } ++ ++ return TRUE; ++ } ++ ++ default: ++ return FALSE; ++ } ++} ++ ++static void ++test_fixture_add_canon(UMockdevTestbedFixture * fixture) ++{ ++ /* Setup first, so we can be sure libusb_open works when the add uevent ++ * happens. ++ */ ++ g_assert_cmpint(umockdev_testbed_attach_ioctl(fixture->testbed, "/dev/bus/usb/001/001", fixture->handler, NULL), ==, 1); ++ ++ /* NOTE: add_device would not create a file, needed for device emulation */ ++ /* XXX: Racy, see https://github.com/martinpitt/umockdev/issues/173 */ ++ umockdev_testbed_add_from_string(fixture->testbed, ++ "P: /devices/usb1\n" ++ "N: bus/usb/001/001\n" ++ "E: SUBSYSTEM=usb\n" ++ "E: DRIVER=usb\n" ++ "E: BUSNUM=001\n" ++ "E: DEVNUM=001\n" ++ "E: DEVNAME=/dev/bus/usb/001/001\n" ++ "E: DEVTYPE=usb_device\n" ++ "A: bConfigurationValue=1\\n\n" ++ "A: busnum=1\\n\n" ++ "A: devnum=1\\n\n" ++ "A: bConfigurationValue=1\\n\n" ++ "A: speed=480\\n\n" ++ /* descriptor from a Canon PowerShot SX200; VID 04a9 PID 31c0 */ ++ "H: descriptors=" ++ "1201000200000040a904c03102000102" ++ "030109022700010100c0010904000003" ++ "06010100070581020002000705020200" ++ "020007058303080009\n", ++ NULL); ++} ++ ++static void ++test_fixture_setup_libusb(UMockdevTestbedFixture * fixture, int devcount) ++{ ++ libusb_device **devs = NULL; ++ ++ libusb_init (&fixture->ctx); ++ ++ /* Supress global log messages completely ++ * (though, in some tests it might be interesting to check there are no real ones). ++ */ ++ libusb_set_log_cb (NULL, log_handler_null, LIBUSB_LOG_CB_GLOBAL); ++ libusb_set_option (fixture->ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG); ++ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, devcount); ++ libusb_free_device_list(devs, TRUE); ++ libusb_set_log_cb (fixture->ctx, log_handler, LIBUSB_LOG_CB_CONTEXT); ++} ++ ++static void ++test_fixture_setup_common(UMockdevTestbedFixture * fixture) ++{ ++ g_assert(cur_fixture == NULL); ++ cur_fixture = fixture; ++ ++ pthread_mutex_init(&fixture->mutex, NULL); ++ ++ fixture->testbed = umockdev_testbed_new(); ++ g_assert(fixture->testbed != NULL); ++ fixture->root_dir = umockdev_testbed_get_root_dir(fixture->testbed); ++ fixture->sys_dir = umockdev_testbed_get_sys_dir(fixture->testbed); ++ ++ fixture->handler = umockdev_ioctl_base_new(); ++ g_object_connect(fixture->handler, "signal-after::handle-ioctl", handle_ioctl_cb, fixture, NULL); ++} ++ ++#if 0 ++static void ++test_fixture_setup_empty(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ /* XXX: Racy, see https://github.com/martinpitt/umockdev/issues/173 */ ++ test_fixture_setup_common(fixture); ++ ++ test_fixture_setup_libusb(fixture, 0); ++} ++#endif ++ ++static void ++test_fixture_setup_with_canon(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ test_fixture_setup_common(fixture); ++ ++ test_fixture_add_canon(fixture); ++ ++ test_fixture_setup_libusb(fixture, 1); ++} ++ ++static void ++test_fixture_teardown(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ g_assert(cur_fixture == fixture); ++ ++ /* libusb_exit logs */ ++ if (fixture->ctx) ++ libusb_exit (fixture->ctx); ++ libusb_set_log_cb (NULL, NULL, LIBUSB_LOG_CB_GLOBAL); ++ cur_fixture = NULL; ++ ++ /* Abort if there are any warnings/errors in the log */ ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); ++ fixture->ctx = NULL; ++ g_assert_null(fixture->libusb_log); ++ ++ g_clear_object(&fixture->handler); ++ g_clear_object(&fixture->testbed); ++ ++ /* verify that temp dir gets cleaned up properly */ ++ g_assert(!g_file_test(fixture->root_dir, G_FILE_TEST_EXISTS)); ++ g_free(fixture->root_dir); ++ g_free(fixture->sys_dir); ++ ++ while (fixture->flying_urbs) { ++ umockdev_ioctl_data_unref (fixture->flying_urbs->data); ++ fixture->flying_urbs = g_list_delete_link (fixture->flying_urbs, fixture->flying_urbs); ++ } ++ ++ pthread_mutex_destroy(&fixture->mutex); ++} ++ ++static void ++test_open_close(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ libusb_device **devs = NULL; ++ struct libusb_device_descriptor desc; ++ libusb_device_handle *handle = NULL; ++ ++ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, 1); ++ /* The linux_enumerate_device may happen from a different thread */ ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_get_device_list"); ++ /* We have exactly one device */ ++ g_assert_cmpint(libusb_get_bus_number(devs[0]), ==, 1); ++ g_assert_cmpint(libusb_get_device_address(devs[0]), ==, 1); ++ ++ /* Get/Check descriptor */ ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); ++ libusb_get_device_descriptor (devs[0], &desc); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_get_device_descriptor"); ++ g_assert_cmpint(desc.idVendor, ==, 0x04a9); ++ g_assert_cmpint(desc.idProduct, ==, 0x31c0); ++ ++ /* Open and close */ ++ g_assert_cmpint(libusb_open(devs[0], &handle), ==, 0); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "usbi_add_event_source"); ++ g_assert_nonnull(handle); ++ libusb_close(handle); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "usbi_remove_event_source"); ++ ++ libusb_free_device_list(devs, TRUE); ++ ++ /* Open and close using vid/pid */ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ libusb_close(handle); ++} ++ ++static void ++test_close_flying(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ UsbChat chat[] = { ++ { ++ .submit = TRUE, ++ .type = USBDEVFS_URB_TYPE_BULK, ++ .endpoint = LIBUSB_ENDPOINT_OUT, ++ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, ++ .buffer_length = 4, ++ }, ++ { .submit = FALSE } ++ }; ++ libusb_device_handle *handle = NULL; ++ struct libusb_transfer *transfer = NULL; ++ ++ fixture->chat = chat; ++ ++ /* Open */ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ ++ transfer = libusb_alloc_transfer(0); ++ libusb_fill_bulk_transfer(transfer, ++ handle, ++ LIBUSB_ENDPOINT_OUT, ++ (unsigned char*) chat[0].buffer, ++ chat[0].buffer_length, ++ NULL, ++ NULL, ++ 1); ++ ++ /* Submit */ ++ libusb_submit_transfer(transfer); ++ ++ /* Closing logs fat error (two lines) */ ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); ++ libusb_close(handle); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*connected as far as we know"); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*cancellation hasn't even been scheduled"); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[do_close\\] Removed transfer"); ++ ++ /* Free'ing the transfer works, and logs to the right context */ ++ libusb_free_transfer(transfer); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[libusb_free_transfer\\]"); ++} ++ ++static void ++test_close_cancelled(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ UsbChat chat[] = { ++ { ++ .submit = TRUE, ++ .type = USBDEVFS_URB_TYPE_BULK, ++ .endpoint = LIBUSB_ENDPOINT_OUT, ++ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, ++ .buffer_length = 4, ++ }, ++ { .submit = FALSE } ++ }; ++ libusb_device_handle *handle = NULL; ++ struct libusb_transfer *transfer = NULL; ++ ++ fixture->chat = chat; ++ ++ /* Open */ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ ++ transfer = libusb_alloc_transfer(0); ++ libusb_fill_bulk_transfer(transfer, ++ handle, ++ LIBUSB_ENDPOINT_OUT, ++ (unsigned char*) chat[0].buffer, ++ chat[0].buffer_length, ++ NULL, ++ NULL, ++ 1); ++ ++ /* Submit */ ++ libusb_submit_transfer(transfer); ++ libusb_cancel_transfer(transfer); ++ ++ /* Closing logs fat error (two lines) */ ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); ++ libusb_close(handle); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*connected as far as we know"); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[do_close\\] .*cancellation.*hasn't completed"); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[do_close\\] Removed transfer"); ++ ++ libusb_free_transfer(transfer); ++} ++ ++static void ++test_ctx_destroy(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ UsbChat chat[] = { ++ { ++ .submit = TRUE, ++ .type = USBDEVFS_URB_TYPE_BULK, ++ .endpoint = LIBUSB_ENDPOINT_OUT, ++ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, ++ .buffer_length = 4, ++ }, ++ { .submit = FALSE } ++ }; ++ libusb_device_handle *handle = NULL; ++ struct libusb_transfer *transfer = NULL; ++ ++ fixture->chat = chat; ++ ++ /* Open */ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ ++ transfer = libusb_alloc_transfer(0); ++ libusb_fill_bulk_transfer(transfer, ++ handle, ++ LIBUSB_ENDPOINT_OUT, ++ (unsigned char*) chat[0].buffer, ++ chat[0].buffer_length, ++ NULL, ++ NULL, ++ 1); ++ ++ /* Submit */ ++ libusb_submit_transfer(transfer); ++ ++ /* Now we are evil and destroy the ctx! */ ++ libusb_exit(fixture->ctx); ++ ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[libusb_exit\\] device.*still referenced"); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[libusb_exit\\] application left some devices open"); ++ ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); ++ fixture->ctx = NULL; ++ ++ /* XXX: Closing crashes the application as it unref's the NULL pointer */ ++ /* libusb_close(handle); */ ++ ++ libusb_free_transfer(transfer); ++} ++ ++static void ++test_get_string_descriptor(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ unsigned char data[255] = { 0, }; ++ libusb_device_handle *handle = NULL; ++ UsbChat chat[] = { ++ { ++ .submit = TRUE, ++ .reaps = &chat[1], ++ .type = USBDEVFS_URB_TYPE_CONTROL, ++ .buffer_length = 12, /* 8 byte out*/ ++ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00", ++ }, { ++ /* String with content 0x0409 (en_US) */ ++ .reap = TRUE, ++ .actual_length = 12, ++ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00\x04\x03\x09\x04", ++ }, { ++ .submit = TRUE, ++ .reaps = &chat[3], ++ .type = USBDEVFS_URB_TYPE_CONTROL, ++ .buffer_length = 263, /* 8 byte out*/ ++ .buffer = (const unsigned char*) "\x80\x06\x01\x03\x09\x04\xff\x00", ++ }, { ++ /* 4 byte string, "ab" */ ++ .reap = TRUE, ++ .actual_length = 14, ++ .buffer = (const unsigned char*) "\x80\x06\x01\x03\x09\x04\xff\x00\x06\x03\x61\x00\x62\x00", ++ }, { ++ .submit = TRUE, ++ .reaps = &chat[5], ++ .type = USBDEVFS_URB_TYPE_CONTROL, ++ .buffer_length = 12, /* 8 byte out*/ ++ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00", ++ }, { ++ .reap = TRUE, ++ .status = -ENOENT, ++ }, { ++ .submit = TRUE, ++ .status = -ENOENT, ++ .type = USBDEVFS_URB_TYPE_CONTROL, ++ .buffer_length = 12, /* 8 byte out*/ ++ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00", ++ }, { ++ .submit = FALSE, ++ } ++ }; ++ ++ fixture->chat = chat; ++ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ ++ /* The chat allows us to fetch the descriptor */ ++ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, 2); ++ g_assert_cmpint(memcmp(data, "ab", 2), ==, 0); ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); ++ ++ /* Again, but the URB fails with ENOENT when reaping */ ++ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, -1); ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); ++ ++ /* Again, but the URB fails to submit with ENOENT */ ++ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, -1); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[submit_control_transfer\\] submiturb failed, errno=2"); ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); ++ ++ libusb_close(handle); ++} ++ ++static void ++transfer_cb_inc_user_data(struct libusb_transfer *transfer) ++{ ++ *(int*)transfer->user_data += 1; ++} ++ ++static void ++test_timeout(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ UsbChat chat[] = { ++ { ++ .submit = TRUE, ++ .type = USBDEVFS_URB_TYPE_BULK, ++ .endpoint = LIBUSB_ENDPOINT_OUT, ++ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, ++ .buffer_length = 4, ++ }, ++ { ++ .submit = FALSE, ++ } ++ }; ++ int completed = 0; ++ libusb_device_handle *handle = NULL; ++ struct libusb_transfer *transfer = NULL; ++ ++ fixture->chat = chat; ++ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ ++ transfer = libusb_alloc_transfer(0); ++ libusb_fill_bulk_transfer(transfer, ++ handle, ++ LIBUSB_ENDPOINT_OUT, ++ (unsigned char*) chat[0].buffer, ++ chat[0].buffer_length, ++ transfer_cb_inc_user_data, ++ &completed, ++ 10); ++ ++ libusb_submit_transfer(transfer); ++ while (!completed) { ++ g_assert_cmpint(libusb_handle_events_completed(fixture->ctx, &completed), ==, 0); ++ /* Silence after one iteration. */ ++ fixture->libusb_log_silence = TRUE; ++ } ++ fixture->libusb_log_silence = FALSE; ++ ++ g_assert_cmpint(transfer->status, ==, LIBUSB_TRANSFER_TIMED_OUT); ++ libusb_free_transfer(transfer); ++ ++ libusb_close(handle); ++} ++ ++#define THREADED_SUBMIT_URB_SETS 64 ++#define THREADED_SUBMIT_URB_IN_FLIGHT 64 ++typedef struct { ++ struct libusb_transfer *transfers[THREADED_SUBMIT_URB_IN_FLIGHT * THREADED_SUBMIT_URB_SETS]; ++ int submitted; ++ int completed; ++ int done; ++ UMockdevTestbedFixture *fixture; ++} TestThreadedSubmit; ++ ++static gpointer ++transfer_submit_all_retry(TestThreadedSubmit *data) ++{ ++ for (guint i = 0; i < G_N_ELEMENTS(data->transfers); i++) { ++ while (libusb_submit_transfer(data->transfers[i]) < 0) { ++ assert_libusb_log_msg(data->fixture, LIBUSB_LOG_LEVEL_ERROR, "submit_bulk_transfer"); ++ continue; ++ } ++ ++ data->submitted += 1; ++ } ++ ++ return NULL; ++} ++ ++static void ++test_threaded_submit_transfer_cb(struct libusb_transfer *transfer) ++{ ++ TestThreadedSubmit *data = transfer->user_data; ++ ++ /* We should only be receiving packets in the main thread */ ++ g_assert_cmpint (getpid(), ==, gettid()); ++ ++ /* Check that the transfer buffer has the expected value */ ++ g_assert_cmpint (*(int*)transfer->buffer, ==, data->completed); ++ data->completed += 1; ++ ++ if (data->completed == G_N_ELEMENTS(data->transfers)) ++ data->done = TRUE; ++} ++ ++static void ++test_threaded_submit(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ GThread *thread = NULL; ++ TestThreadedSubmit data = { .fixture = fixture }; ++ UsbChat out_msg = { ++ .submit = TRUE, ++ .type = USBDEVFS_URB_TYPE_BULK, ++ .endpoint = LIBUSB_ENDPOINT_IN, ++ .buffer_length = sizeof(int), ++ }; ++ UsbChat in_msg = { ++ .reap = TRUE, ++ .actual_length = 4, ++ }; ++ UsbChat *c; ++ libusb_device_handle *handle = NULL; ++ int urb; ++ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ ++ fixture->libusb_log_silence = TRUE; ++ ++ c = fixture->chat = g_new0(UsbChat, G_N_ELEMENTS(data.transfers) * 2 + 1); ++ urb = 0; ++ for (int i = 0; i < THREADED_SUBMIT_URB_SETS; i++) { ++ for (int j = 0; j < THREADED_SUBMIT_URB_IN_FLIGHT; j++) { ++ c[i*2*THREADED_SUBMIT_URB_IN_FLIGHT + j] = out_msg; ++ c[i*2*THREADED_SUBMIT_URB_IN_FLIGHT + j].reaps = &c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j]; ++ c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j] = in_msg; ++ c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j].buffer = (unsigned char*) g_new0(int, 1); ++ *(int*) c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j].buffer = urb; ++ ++ data.transfers[urb] = libusb_alloc_transfer(0); ++ libusb_fill_bulk_transfer(data.transfers[urb], ++ handle, ++ LIBUSB_ENDPOINT_IN, ++ g_malloc(out_msg.buffer_length), ++ out_msg.buffer_length, ++ test_threaded_submit_transfer_cb, ++ &data, ++ G_MAXUINT); ++ data.transfers[urb]->flags = LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; ++ urb++; ++ } ++ } ++ ++ thread = g_thread_new("transfer all", (GThreadFunc) transfer_submit_all_retry, &data); ++ ++ while (!data.done) ++ g_assert_cmpint(libusb_handle_events_completed(fixture->ctx, &data.done), ==, 0); ++ ++ g_thread_join(thread); ++ ++ fixture->libusb_log_silence = FALSE; ++ libusb_close(handle); ++ ++ for (int i = 0; i < 2 * THREADED_SUBMIT_URB_SETS * THREADED_SUBMIT_URB_SETS; i++) ++ g_clear_pointer ((char**) &c->buffer, g_free); ++ g_free (c); ++} ++ ++int ++main(int argc, char **argv) ++{ ++ g_test_init(&argc, &argv, NULL); ++ ++ g_test_add("/libusb/open-close", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_open_close, ++ test_fixture_teardown); ++ ++ g_test_add("/libusb/close-flying", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_close_flying, ++ test_fixture_teardown); ++ g_test_add("/libusb/close-cancelled", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_close_cancelled, ++ test_fixture_teardown); ++ ++ g_test_add("/libusb/ctx-destroy", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_ctx_destroy, ++ test_fixture_teardown); ++ ++ g_test_add("/libusb/string-descriptor", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_get_string_descriptor, ++ test_fixture_teardown); ++ ++ g_test_add("/libusb/timeout", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_timeout, ++ test_fixture_teardown); ++ ++ g_test_add("/libusb/threaded-submit", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_threaded_submit, ++ test_fixture_teardown); ++ ++ return g_test_run(); ++} +-- +2.35.1 + diff --git a/libusb1.spec b/libusb1.spec index 901fbd2..b35bb98 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -6,6 +6,7 @@ Source0: https://github.com/libusb/libusb/releases/download/v%{version}/l License: LGPLv2+ URL: http://libusb.info BuildRequires: systemd-devel doxygen libtool +BuildRequiers: umockdev BuildRequires: make BuildRequires: gcc # libusbx was removed in F34 @@ -17,6 +18,8 @@ Obsoletes: libusbx < %{version}-%{release} Patch0001: https://github.com/libusb/libusb/pull/1058.patch # Fix a crash if a transfer outlives closing the device Patch0002: https://github.com/libusb/libusb/pull/1073.patch +# Add umockdev based tests from https://github.com/libusb/libusb/pull/1078 +Patch0003: 0001-tests-Add-some-umockdev-based-tests.patch %description This package provides a way for applications to access USB devices. @@ -82,6 +85,7 @@ popd %{make_install} mkdir -p $RPM_BUILD_ROOT%{_bindir} install -m 755 tests/.libs/stress $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress +install -m 755 tests/.libs/umockdev $RPM_BUILD_ROOT%{_bindir}/libusb-test-umockdev install -m 755 examples/.libs/testlibusb \ $RPM_BUILD_ROOT%{_bindir}/libusb-test-libusb # Some examples are very device-specific / require specific hw and miss --help @@ -96,6 +100,7 @@ rm $RPM_BUILD_ROOT%{_libdir}/*.la %check LD_LIBRARY_PATH=libusb/.libs ldd $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress +LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-umockdev LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-libusb LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-example-listdevs @@ -121,6 +126,7 @@ LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-example-listdevs %{_bindir}/libusb-example-listdevs %{_bindir}/libusb-example-xusb %{_bindir}/libusb-test-stress +%{_bindir}/libusb-test-umockdev %{_bindir}/libusb-test-libusb From 261c00aec299611e2b884d630468dbc71745e148 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 22 Feb 2022 12:06:52 +0100 Subject: [PATCH 12/42] Work around API misuse in gutenprint https://bugzilla.redhat.com/show_bug.cgi?id=2056326 --- ...ll-first-context-as-implicit-default.patch | 175 ++++++++++++++++++ libusb1.spec | 4 + 2 files changed, 179 insertions(+) create mode 100644 0001-core-Install-first-context-as-implicit-default.patch diff --git a/0001-core-Install-first-context-as-implicit-default.patch b/0001-core-Install-first-context-as-implicit-default.patch new file mode 100644 index 0000000..b04207b --- /dev/null +++ b/0001-core-Install-first-context-as-implicit-default.patch @@ -0,0 +1,175 @@ +From f74f1e21b6201ec803347ddaf2737cdaf9a1180a Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Tue, 22 Feb 2022 11:45:38 +0100 +Subject: [PATCH] core: Install first context as implicit default + +There was a behaviour change in libusb, which triggers issues when the +API is misused. This caused gutenprint to crash, see +https://bugzilla.redhat.com/show_bug.cgi?id=2055504 + +For now, work around this by installing an implicit default. But, change +the code to log an error in case this "feature" is being used. +--- + libusb/core.c | 24 ++++++++++++++++-------- + libusb/libusbi.h | 8 +++++++- + tests/umockdev.c | 26 ++++++++++++++++++++++++++ + 3 files changed, 49 insertions(+), 9 deletions(-) + +diff --git a/libusb/core.c b/libusb/core.c +index 1c1ada1..c56df72 100644 +--- a/libusb/core.c ++++ b/libusb/core.c +@@ -41,7 +41,7 @@ static libusb_log_cb log_handler; + #endif + + struct libusb_context *usbi_default_context; +-static int default_context_refcnt; ++int usbi_default_context_refcnt; + static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; + static struct usbi_option default_context_options[LIBUSB_OPTION_MAX]; + +@@ -2284,9 +2284,9 @@ int API_EXPORTED libusb_init(libusb_context **ctx) + + usbi_mutex_static_lock(&default_context_lock); + +- if (!ctx && usbi_default_context) { ++ if (!ctx && usbi_default_context_refcnt > 0) { + usbi_dbg(usbi_default_context, "reusing default context"); +- default_context_refcnt++; ++ usbi_default_context_refcnt++; + usbi_mutex_static_unlock(&default_context_lock); + return 0; + } +@@ -2331,7 +2331,7 @@ int API_EXPORTED libusb_init(libusb_context **ctx) + /* default context must be initialized before calling usbi_dbg */ + if (!ctx) { + usbi_default_context = _ctx; +- default_context_refcnt = 1; ++ usbi_default_context_refcnt = 1; + usbi_dbg(usbi_default_context, "created default context"); + } + +@@ -2357,6 +2357,12 @@ int API_EXPORTED libusb_init(libusb_context **ctx) + if (ctx) + *ctx = _ctx; + ++ if (usbi_default_context == NULL) { ++ usbi_default_context = _ctx; ++ usbi_default_context_refcnt = 0; ++ usbi_dbg(usbi_default_context, "installing new context as implicit default"); ++ } ++ + usbi_mutex_static_unlock(&default_context_lock); + + return 0; +@@ -2373,7 +2379,7 @@ err_free_ctx: + if (!ctx) { + /* clear default context that was not fully initialized */ + usbi_default_context = NULL; +- default_context_refcnt = 0; ++ usbi_default_context_refcnt = 0; + } + + usbi_mutex_destroy(&_ctx->open_devs_lock); +@@ -2407,7 +2413,7 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) + return; + } + +- if (--default_context_refcnt > 0) { ++ if (--usbi_default_context_refcnt > 0) { + usbi_dbg(ctx, "not destroying default context"); + usbi_mutex_static_unlock(&default_context_lock); + return; +@@ -2427,8 +2433,10 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) + if (usbi_backend.exit) + usbi_backend.exit(_ctx); + +- if (!ctx) ++ if (!ctx || ctx == usbi_default_context) { + usbi_default_context = NULL; ++ assert(usbi_default_context_refcnt == 0); ++ } + + usbi_mutex_static_unlock(&default_context_lock); + +@@ -2575,7 +2583,7 @@ static void log_v(struct libusb_context *ctx, enum libusb_log_level level, + #else + enum libusb_log_level ctx_level; + +- ctx = usbi_get_context(ctx); ++ ctx = ctx ? ctx : usbi_default_context; + if (ctx) + ctx_level = ctx->debug; + else +diff --git a/libusb/libusbi.h b/libusb/libusbi.h +index 5f0d5c2..a404cff 100644 +--- a/libusb/libusbi.h ++++ b/libusb/libusbi.h +@@ -436,13 +436,19 @@ struct libusb_context { + }; + + extern struct libusb_context *usbi_default_context; ++extern int usbi_default_context_refcnt; + + extern struct list_head active_contexts_list; + extern usbi_mutex_static_t active_contexts_lock; + + static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx) + { +- return ctx ? ctx : usbi_default_context; ++ if (!ctx && usbi_default_context) { ++ if (usbi_default_context_refcnt == 0) ++ usbi_err(usbi_default_context, "API misuse! Using non-default context as implicit default."); ++ ctx = usbi_default_context; ++ } ++ return ctx; + } + + enum usbi_event_flags { +diff --git a/tests/umockdev.c b/tests/umockdev.c +index b2af512..7de0aae 100644 +--- a/tests/umockdev.c ++++ b/tests/umockdev.c +@@ -551,6 +551,27 @@ test_open_close(UMockdevTestbedFixture * fixture, UNUSED_DATA) + libusb_close(handle); + } + ++static void ++test_implicit_default(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ libusb_device **devs = NULL; ++ ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); ++ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); ++ libusb_free_device_list(devs, TRUE); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[usbi_get_context\\].*implicit default"); ++ ++ libusb_init(NULL); ++ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); ++ libusb_exit(NULL); ++ ++ /* We free late, causing a warning from libusb_exit. However, ++ * we never see this warning (i.e. test success) because it is on a ++ * different context. ++ */ ++ libusb_free_device_list(devs, TRUE); ++} ++ + static void + test_close_flying(UMockdevTestbedFixture * fixture, UNUSED_DATA) + { +@@ -932,6 +953,11 @@ main(int argc, char **argv) + test_open_close, + test_fixture_teardown); + ++ g_test_add("/libusb/implicit-default", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_implicit_default, ++ test_fixture_teardown); ++ + g_test_add("/libusb/close-flying", UMockdevTestbedFixture, NULL, + test_fixture_setup_with_canon, + test_close_flying, +-- +2.35.1 + diff --git a/libusb1.spec b/libusb1.spec index b35bb98..a97ff41 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -21,6 +21,10 @@ Patch0002: https://github.com/libusb/libusb/pull/1073.patch # Add umockdev based tests from https://github.com/libusb/libusb/pull/1078 Patch0003: 0001-tests-Add-some-umockdev-based-tests.patch +# Work around API misuse in gutenprint +# https://bugzilla.redhat.com/show_bug.cgi?id=2056326 +Patch9999: 0001-core-Install-first-context-as-implicit-default.patch + %description This package provides a way for applications to access USB devices. From 36cafe8c9fa82c5fa59c42877230f7fd120ffa93 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 22 Feb 2022 12:02:16 +0100 Subject: [PATCH 13/42] Update regression fix patch for late transfer free's Resolves: #2056326 --- 1073.patch | 183 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 122 insertions(+), 61 deletions(-) diff --git a/1073.patch b/1073.patch index 8b91a48..7ecce5b 100644 --- a/1073.patch +++ b/1073.patch @@ -1,72 +1,133 @@ -From bf833ee6adf58bd4a4a468aa729cdc78bdc13ede Mon Sep 17 00:00:00 2001 +From 1cb11c5ba0d1d1266fe4ddd70f29d081f9d16802 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 15 Feb 2022 11:13:41 +0100 -Subject: [PATCH 1/2] core: Catch NULL dev_handle when getting a transfer's - context +Subject: [PATCH] io: Track device in usbi_transfer -The dev_handle will be set to NULL when the transfer is still in-flight -while the device is closed. In that case, the transfer free function -will try to access the context and would run into a NULL pointer -dereference. +transfer->dev_handle currently has the behaviour that it will be unset +if the device is closed. The sync API uses this fact to catch an error +case. -Add a test for dev_handle being valid before dereferencing it further. +In other cases, transfer->dev_handle will keep its value, which means +that if the transfer lives longer than the device handle, the pointer +becomes invalid. + +The transfer does however keep a reference to the device, which owns the +pointer to the context. As such, we can track this reference internal to +the transfer, and it is set while the transfer is in-flight. + +With this, switch the logging infrastructure to use itransfer->dev->ctx +while checking that itransfer->dev is non-NULL. + +Note that this was a regression caused by 6cae9c6dbd74 ("core: update +usbi_dbg to take the context as an argument"), specifically when +resolving the context while freeing a transfer after closing a device. + +Note that the transfer will now keep a reference to the device until it +is free'ed. This allows it to use the correct context for logging even +in libusb_free_transfer. + +The alternative to all this would be to just explicitly pass NULL to the +log handler in libusb_free_transfer. --- - libusb/libusbi.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/libusb/libusbi.h b/libusb/libusbi.h -index 158a9af5..dde43df2 100644 ---- a/libusb/libusbi.h -+++ b/libusb/libusbi.h -@@ -330,7 +330,7 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, - - #define DEVICE_CTX(dev) ((dev)->ctx) - #define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) --#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) -+#define TRANSFER_CTX(transfer) ((transfer)->dev_handle ? HANDLE_CTX((transfer)->dev_handle) : NULL) - #define ITRANSFER_CTX(itransfer) \ - (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer))) - - -From 6428090ea77dfb80906a146977ea7fd6de4718c8 Mon Sep 17 00:00:00 2001 -From: Benjamin Berg -Date: Tue, 15 Feb 2022 10:59:00 +0100 -Subject: [PATCH 2/2] io: Unset dev_handle when removing transfer from flying - list - -API users might hold on to transfers a bit longer than they are in the -flying list. If they then close the device prior to freeing all -transfers, we would end up with invalid pointers to the device. - -Fix this by setting the device handle to NULL when removing the device -from the flying list. This matches the behaviour when the device is -closed while the transfer is still in the flying list. - -Specifically, the libgusb wrapper will currently only free the -underlying transfer in a later mainloop iteration (as a side effect on -how GTask does memory management). It is possible to fix this, but it -would make memory management within libgusb much more error prone. ---- - libusb/io.c | 2 ++ - 1 file changed, 2 insertions(+) + libusb/io.c | 20 ++++++++++++-------- + libusb/libusbi.h | 10 +++++++--- + 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/libusb/io.c b/libusb/io.c -index 0d2ac9ea..4e6d8984 100644 +index 0d2ac9ea..b919e9d9 100644 --- a/libusb/io.c +++ b/libusb/io.c -@@ -1456,6 +1456,7 @@ static int add_to_flying_list(struct usbi_transfer *itransfer) - * if it fails to update the timer for the next timeout. */ - static int remove_from_flying_list(struct usbi_transfer *itransfer) - { -+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - struct libusb_context *ctx = ITRANSFER_CTX(itransfer); - int rearm_timer; - int r = 0; -@@ -1466,6 +1467,7 @@ static int remove_from_flying_list(struct usbi_transfer *itransfer) - list_del(&itransfer->list); - if (rearm_timer) - r = arm_timer_for_next_timeout(ctx); -+ transfer->dev_handle = NULL; - usbi_mutex_unlock(&ctx->flying_transfers_lock); +@@ -1344,6 +1344,8 @@ void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer) + itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + usbi_mutex_destroy(&itransfer->lock); ++ if (itransfer->dev) ++ libusb_unref_device(itransfer->dev); + + priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); + ptr = (unsigned char *)itransfer - priv_size; +@@ -1489,9 +1491,15 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) + { + struct usbi_transfer *itransfer = + LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); +- struct libusb_context *ctx = TRANSFER_CTX(transfer); ++ struct libusb_context *ctx; + int r; + ++ assert(transfer->dev_handle); ++ if (itransfer->dev) ++ libusb_unref_device(itransfer->dev); ++ itransfer->dev = libusb_ref_device(transfer->dev_handle->dev); ++ ++ ctx = HANDLE_CTX(transfer->dev_handle); + usbi_dbg(ctx, "transfer %p", transfer); + + /* +@@ -1551,8 +1559,6 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) + r = usbi_backend.submit_transfer(itransfer); + if (r == LIBUSB_SUCCESS) { + itransfer->state_flags |= USBI_TRANSFER_IN_FLIGHT; +- /* keep a reference to this device */ +- libusb_ref_device(transfer->dev_handle->dev); + } + usbi_mutex_unlock(&itransfer->lock); + +@@ -1659,7 +1665,6 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + { + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); +- struct libusb_device_handle *dev_handle = transfer->dev_handle; + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); + uint8_t flags; + int r; +@@ -1693,7 +1698,6 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, + * this point. */ + if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) + libusb_free_transfer(transfer); +- libusb_unref_device(dev_handle->dev); return r; + } + +@@ -1727,10 +1731,10 @@ int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer) + * function will be called the next time an event handler runs. */ + void usbi_signal_transfer_completion(struct usbi_transfer *itransfer) + { +- libusb_device_handle *dev_handle = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->dev_handle; ++ struct libusb_device *dev = itransfer->dev; + +- if (dev_handle) { +- struct libusb_context *ctx = HANDLE_CTX(dev_handle); ++ if (dev) { ++ struct libusb_context *ctx = DEVICE_CTX(dev); + unsigned int event_flags; + + usbi_mutex_lock(&ctx->event_data_lock); +diff --git a/libusb/libusbi.h b/libusb/libusbi.h +index 158a9af5..5f0d5c2e 100644 +--- a/libusb/libusbi.h ++++ b/libusb/libusbi.h +@@ -329,10 +329,11 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, + #endif /* ENABLE_LOGGING */ + + #define DEVICE_CTX(dev) ((dev)->ctx) +-#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) +-#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) ++#define HANDLE_CTX(handle) ((handle) ? DEVICE_CTX((handle)->dev) : NULL) + #define ITRANSFER_CTX(itransfer) \ +- (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer))) ++ ((itransfer)->dev ? DEVICE_CTX((itransfer)->dev) : NULL) ++#define TRANSFER_CTX(transfer) \ ++ (ITRANSFER_CTX(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer))) + + #define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN)) + #define IS_EPOUT(ep) (!IS_EPIN(ep)) +@@ -562,6 +563,9 @@ struct usbi_transfer { + uint32_t state_flags; /* Protected by usbi_transfer->lock */ + uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */ + ++ /* This is used for logging mostly. As long as it is set, the */ ++ struct libusb_device *dev; ++ + /* this lock is held during libusb_submit_transfer() and + * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate + * cancellation, submission-during-cancellation, etc). the OS backend From e1239cc0467cc792b0ebc8d89d335532849e5e66 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 22 Feb 2022 12:05:43 +0100 Subject: [PATCH 14/42] Add umockdev based tests See https://github.com/libusb/libusb/pull/1078 --- ...-tests-Add-some-umockdev-based-tests.patch | 1038 +++++++++++++++++ libusb1.spec | 6 + tests/run.sh | 1 + 3 files changed, 1045 insertions(+) create mode 100644 0001-tests-Add-some-umockdev-based-tests.patch diff --git a/0001-tests-Add-some-umockdev-based-tests.patch b/0001-tests-Add-some-umockdev-based-tests.patch new file mode 100644 index 0000000..8aba02d --- /dev/null +++ b/0001-tests-Add-some-umockdev-based-tests.patch @@ -0,0 +1,1038 @@ +From 87976d513fbb35e13cbcd8cb00d2abc1cac0dd54 Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Fri, 18 Feb 2022 16:26:29 +0100 +Subject: [PATCH 1/2] tests: Add some umockdev based tests + +Add some basic umockdev tests. The setup in this case is all in one +process, but umockdev in principle could also work between processes. + +This is just a start, more tests would make sense. +--- + configure.ac | 7 + + tests/Makefile.am | 13 +- + tests/umockdev.c | 965 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 984 insertions(+), 1 deletion(-) + create mode 100644 tests/umockdev.c + +diff --git a/configure.ac b/configure.ac +index f6cf2f9..5336463 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -177,6 +177,12 @@ linux) + dnl system has udev. use it or fail! + AC_CHECK_HEADER([libudev.h], [], [AC_MSG_ERROR([udev support requested but libudev header not installed])]) + AC_CHECK_LIB([udev], [udev_new], [], [AC_MSG_ERROR([udev support requested but libudev not installed])]) ++ ++ # We can build umockdev tests (if available) ++ PKG_PROG_PKG_CONFIG ++ PKG_CHECK_MODULES(UMOCKDEV, umockdev-1.0 >= 0.16.0, ac_have_umockdev=yes, ac_have_umockdev=no) ++ AC_SUBST(UMOCKDEV_CFLAGS) ++ AC_SUBST(UMOCKDEV_LIBS) + else + AC_CHECK_HEADERS([asm/types.h]) + AC_CHECK_HEADER([linux/netlink.h], [], [AC_MSG_ERROR([Linux netlink header not found])]) +@@ -356,6 +362,7 @@ AC_ARG_ENABLE([tests-build], + + AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" != xno]) + AM_CONDITIONAL([BUILD_TESTS], [test "x$build_tests" != xno]) ++AM_CONDITIONAL([BUILD_UMOCKDEV_TEST], [test "x$ac_have_umockdev" = xyes -a "x$log_enabled" != xno]) + AM_CONDITIONAL([CREATE_IMPORT_LIB], [test "x$create_import_lib" = xyes]) + AM_CONDITIONAL([OS_DARWIN], [test "x$backend" = xdarwin]) + AM_CONDITIONAL([OS_HAIKU], [test "x$backend" = xhaiku]) +diff --git a/tests/Makefile.am b/tests/Makefile.am +index cb8fad9..cf6237e 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -2,6 +2,17 @@ AM_CPPFLAGS = -I$(top_srcdir)/libusb + LDADD = ../libusb/libusb-1.0.la + LIBS = + ++stress_SOURCES = stress.c libusb_testlib.h testlib.c ++ + noinst_PROGRAMS = stress + +-stress_SOURCES = stress.c libusb_testlib.h testlib.c ++if BUILD_UMOCKDEV_TEST ++# NOTE: We add libumockdev-preload.so so that we can run tests in-process ++# We also use -Wl,-lxxx as the compiler doesn't need it and libtool ++# would reorder the flags otherwise. ++umockdev_CPPFLAGS = ${UMOCKDEV_CFLAGS} -I$(top_srcdir)/libusb ++umockdev_LDFLAGS = -Wl,--push-state,--no-as-needed -Wl,-lumockdev-preload -Wl,--pop-state ${UMOCKDEV_LIBS} ++umockdev_SOURCES = umockdev.c ++ ++noinst_PROGRAMS += umockdev ++endif +diff --git a/tests/umockdev.c b/tests/umockdev.c +new file mode 100644 +index 0000000..c026b51 +--- /dev/null ++++ b/tests/umockdev.c +@@ -0,0 +1,965 @@ ++/* ++ * libusb umockdev based tests ++ * ++ * Copyright (C) 2022 Benjamin Berg ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program; If not, see . ++ */ ++ ++#define _GNU_SOURCE ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "libusb.h" ++ ++#include "umockdev.h" ++ ++#define UNUSED_DATA __attribute__ ((unused)) gconstpointer unused_data ++ ++/* avoid leak reports inside assertions; leaking stuff on assertion failures does not matter in tests */ ++#if !defined(__clang__) ++#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak" ++#pragma GCC diagnostic ignored "-Wanalyzer-file-leak" ++#endif ++ ++typedef struct { ++ pid_t thread; ++ libusb_context *ctx; ++ enum libusb_log_level level; ++ char *str; ++} LogMessage; ++ ++static void ++log_message_free(LogMessage *msg) ++{ ++ g_free(msg->str); ++ g_free(msg); ++} ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(LogMessage, log_message_free) ++ ++typedef struct _UsbChat UsbChat; ++ ++struct _UsbChat { ++ gboolean submit; ++ gboolean reap; ++ UsbChat *reaps; ++ UsbChat *next; ++ ++ /* struct usbdevfs_urb */ ++ unsigned char type; ++ unsigned char endpoint; ++ int status; ++ unsigned int flags; ++ const unsigned char *buffer; ++ int buffer_length; ++ int actual_length; ++ ++ /* */ ++ UMockdevIoctlData *submit_urb; ++}; ++ ++typedef struct { ++ UMockdevTestbed *testbed; ++ UMockdevIoctlBase *handler; ++ struct libusb_context *ctx; ++ ++ gchar *root_dir; ++ gchar *sys_dir; ++ ++ gboolean libusb_log_silence; ++ GList *libusb_log; ++ ++ UsbChat *chat; ++ GList *flying_urbs; ++ GList *discarded_urbs; ++ ++ /* GMutex confuses tsan unecessarily */ ++ pthread_mutex_t mutex; ++} UMockdevTestbedFixture; ++ ++/* Global for log handler */ ++static UMockdevTestbedFixture *cur_fixture = NULL; ++ ++static void ++log_handler(libusb_context *ctx, enum libusb_log_level level, const char *str) ++{ ++ /* May be called from different threads without synchronization! */ ++ LogMessage *msg; ++ pid_t tid = gettid(); ++ ++ g_assert (cur_fixture != NULL); ++ g_assert(pthread_mutex_lock(&cur_fixture->mutex) == 0); ++ ++ msg = g_new0(LogMessage, 1); ++ msg->ctx = ctx; ++ msg->level = level; ++ msg->str = g_strchomp (g_strdup(str)); ++ msg->thread = tid; ++ ++ if (!cur_fixture->libusb_log_silence) ++ g_printerr("%s\n", msg->str); ++ ++ cur_fixture->libusb_log = g_list_append(cur_fixture->libusb_log, msg); ++ pthread_mutex_unlock(&cur_fixture->mutex); ++} ++ ++static void ++log_handler_null(libusb_context *ctx, enum libusb_log_level level, const char *str) ++{ ++ (void) ctx; ++ (void) level; ++ (void) str; ++} ++ ++static void ++clear_libusb_log(UMockdevTestbedFixture * fixture, enum libusb_log_level level) ++{ ++ g_assert(pthread_mutex_lock(&fixture->mutex) == 0); ++ ++ while (fixture->libusb_log) { ++ LogMessage *msg = fixture->libusb_log->data; ++ ++ g_assert(msg->ctx == fixture->ctx); ++ ++ if (msg->level < level) { ++ pthread_mutex_unlock(&fixture->mutex); ++ return; ++ } ++ ++ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log); ++ log_message_free(msg); ++ } ++ pthread_mutex_unlock(&fixture->mutex); ++} ++ ++static void ++assert_libusb_log_msg(UMockdevTestbedFixture * fixture, enum libusb_log_level level, const char *re) ++{ ++ g_assert(pthread_mutex_lock(&fixture->mutex) == 0); ++ ++ while (fixture->libusb_log) { ++ g_autoptr(LogMessage) msg = NULL; ++ ++ if (fixture->libusb_log == NULL) ++ g_error ("No level %d message found searching for %s", level, re); ++ ++ msg = fixture->libusb_log->data; ++ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log); ++ ++ if (msg->ctx != fixture->ctx) ++ g_error ("Saw unexpected message \"%s\" from context %p while %p was expected", ++ msg->str, msg->ctx, fixture->ctx); ++ ++ if (msg->level == level && g_regex_match_simple(re, msg->str, 0, 0)) { ++ pthread_mutex_unlock(&fixture->mutex); ++ return; ++ } ++ ++ /* Allow skipping INFO and DEBUG messages */ ++ if (msg->level >= LIBUSB_LOG_LEVEL_INFO) ++ continue; ++ ++ g_error ("Searched for \"%s\" (%d) but found \"%s\" (%d)", re, level, msg->str, msg->level); ++ } ++ ++ pthread_mutex_unlock(&fixture->mutex); ++ g_error ("Searched for \"%s\" (%d) but no message matched", re, level); ++} ++ ++#if 0 ++static void ++assert_libusb_no_log_msg(UMockdevTestbedFixture * fixture, enum libusb_log_level level, const char *re) ++{ ++ g_assert(pthread_mutex_lock(&fixture->mutex) == 0); ++ ++ while (fixture->libusb_log) { ++ g_autoptr(LogMessage) msg = NULL; ++ gboolean matching; ++ ++ msg = fixture->libusb_log->data; ++ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log); ++ ++ g_assert(msg->ctx == fixture->ctx); ++ ++ matching = (msg->level == level && g_regex_match_simple(re, msg->str, 0, 0)); ++ ++ /* Allow skipping INFO and DEBUG messages */ ++ if (!matching && msg->level >= LIBUSB_LOG_LEVEL_INFO) ++ continue; ++ ++ g_error ("Asserting \"%s\" (%d) not logged and found \"%s\" (%d)", re, level, msg->str, msg->level); ++ } ++ ++ pthread_mutex_unlock(&fixture->mutex); ++} ++#endif ++ ++static void ++dump_buffer(const unsigned char *buffer, int len) ++{ ++ g_autoptr(GString) line = NULL; ++ ++ line = g_string_new (""); ++ for (gint i = 0; i < len; i++) { ++ g_string_append_printf(line, "%02x ", buffer[i]); ++ if ((i + 1) % 16 == 0) { ++ g_printerr(" %s\n", line->str); ++ g_string_set_size(line, 0); ++ } ++ } ++ ++ if (line->len) ++ g_printerr(" %s\n", line->str); ++} ++ ++static gint ++cmp_ioctl_data_addr(const void *data, const void *addr) ++{ ++ return ((const UMockdevIoctlData*) data)->client_addr != (gulong) addr; ++} ++ ++static gboolean ++handle_ioctl_cb (UMockdevIoctlBase *handler, UMockdevIoctlClient *client, UMockdevTestbedFixture *fixture) ++{ ++ UMockdevIoctlData *ioctl_arg; ++ long int request; ++ struct usbdevfs_urb *urb; ++ ++ (void) handler; ++ ++ request = umockdev_ioctl_client_get_request (client); ++ ioctl_arg = umockdev_ioctl_client_get_arg (client); ++ ++ /* NOTE: We share the address space, dereferencing pointers *will* work. ++ * However, to make tsan work, we still stick to the API that resolves ++ * the data into a local copy! */ ++ ++ switch (request) { ++ case USBDEVFS_GET_CAPABILITIES: { ++ g_autoptr(UMockdevIoctlData) d = NULL; ++ d = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(guint32), NULL); ++ ++ *(guint32*) d->data = USBDEVFS_CAP_BULK_SCATTER_GATHER | ++ USBDEVFS_CAP_BULK_CONTINUATION | ++ USBDEVFS_CAP_NO_PACKET_SIZE_LIM | ++ USBDEVFS_CAP_REAP_AFTER_DISCONNECT | ++ USBDEVFS_CAP_ZERO_PACKET; ++ ++ umockdev_ioctl_client_complete(client, 0, 0); ++ return TRUE; ++ } ++ ++ case USBDEVFS_CLAIMINTERFACE: ++ case USBDEVFS_RELEASEINTERFACE: ++ case USBDEVFS_CLEAR_HALT: ++ case USBDEVFS_RESET: ++ case USBDEVFS_RESETEP: ++ umockdev_ioctl_client_complete(client, 0, 0); ++ return TRUE; ++ ++ case USBDEVFS_SUBMITURB: { ++ g_autoptr(UMockdevIoctlData) urb_buffer = NULL; ++ g_autoptr(UMockdevIoctlData) urb_data = NULL; ++ gsize buflen; ++ ++ if (!fixture->chat || !fixture->chat->submit) ++ return FALSE; ++ ++ buflen = fixture->chat->buffer_length; ++ if (fixture->chat->type == USBDEVFS_URB_TYPE_CONTROL) ++ buflen = 8; ++ ++ urb_data = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(struct usbdevfs_urb), NULL); ++ urb = (struct usbdevfs_urb*) urb_data->data; ++ urb_buffer = umockdev_ioctl_data_resolve(urb_data, G_STRUCT_OFFSET(struct usbdevfs_urb, buffer), urb->buffer_length, NULL); ++ ++ if (fixture->chat->type == urb->type && ++ fixture->chat->endpoint == urb->endpoint && ++ fixture->chat->buffer_length == urb->buffer_length && ++ (fixture->chat->buffer == NULL || memcmp (fixture->chat->buffer, urb_buffer->data, buflen) == 0)) { ++ fixture->flying_urbs = g_list_append (fixture->flying_urbs, umockdev_ioctl_data_ref(urb_data)); ++ ++ if (fixture->chat->reaps) ++ fixture->chat->reaps->submit_urb = urb_data; ++ ++ if (fixture->chat->status) ++ umockdev_ioctl_client_complete(client, -1, -fixture->chat->status); ++ else ++ umockdev_ioctl_client_complete(client, 0, 0); ++ ++ if (fixture->chat->next) ++ fixture->chat = fixture->chat->next; ++ else ++ fixture->chat += 1; ++ return TRUE; ++ } ++ ++ /* chat message didn't match, don't accept it */ ++ g_printerr("Could not process submit urb:\n"); ++ g_printerr(" t: %d, ep: %d, actual_length: %d, buffer_length: %d\n", ++ urb->type, urb->endpoint, urb->actual_length, urb->buffer_length); ++ if (urb->type == USBDEVFS_URB_TYPE_CONTROL || urb->endpoint & LIBUSB_ENDPOINT_IN) ++ dump_buffer(urb->buffer, urb->buffer_length); ++ g_printerr("Looking for:\n"); ++ g_printerr(" t: %d, ep: %d, actual_length: %d, buffer_length: %d\n", ++ fixture->chat->type, fixture->chat->endpoint, ++ fixture->chat->actual_length, fixture->chat->buffer_length); ++ if (fixture->chat->buffer) ++ dump_buffer(fixture->chat->buffer, buflen); ++ ++ return FALSE; ++ } ++ ++ case USBDEVFS_REAPURB: ++ case USBDEVFS_REAPURBNDELAY: { ++ g_autoptr(UMockdevIoctlData) urb_ptr = NULL; ++ g_autoptr(UMockdevIoctlData) urb_data = NULL; ++ ++ if (fixture->discarded_urbs) { ++ urb_data = fixture->discarded_urbs->data; ++ urb = (struct usbdevfs_urb*) urb_data->data; ++ fixture->discarded_urbs = g_list_delete_link(fixture->discarded_urbs, fixture->discarded_urbs); ++ urb->status = -ENOENT; ++ ++ urb_ptr = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(gpointer), NULL); ++ umockdev_ioctl_data_set_ptr(urb_ptr, 0, urb_data); ++ ++ umockdev_ioctl_client_complete(client, 0, 0); ++ return TRUE; ++ } ++ ++ if (fixture->chat && fixture->chat->reap) { ++ GList *l = g_list_find(fixture->flying_urbs, fixture->chat->submit_urb); ++ ++ if (l) { ++ fixture->flying_urbs = g_list_remove_link(fixture->flying_urbs, fixture->flying_urbs); ++ ++ urb_data = fixture->chat->submit_urb; ++ urb = (struct usbdevfs_urb*) urb_data->data; ++ urb->actual_length = fixture->chat->actual_length; ++ if (urb->type == USBDEVFS_URB_TYPE_CONTROL && urb->actual_length) ++ urb->actual_length -= 8; ++ if (fixture->chat->buffer) ++ memcpy(urb->buffer, fixture->chat->buffer, fixture->chat->actual_length); ++ urb->status = fixture->chat->status; ++ ++ urb_ptr = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(gpointer), NULL); ++ umockdev_ioctl_data_set_ptr(urb_ptr, 0, urb_data); ++ if (fixture->chat->next) ++ fixture->chat = fixture->chat->next; ++ else ++ fixture->chat += 1; ++ umockdev_ioctl_client_complete(client, 0, 0); ++ return TRUE; ++ } ++ } ++ ++ /* Nothing to reap */ ++ umockdev_ioctl_client_complete(client, -1, EAGAIN); ++ return TRUE; ++ } ++ ++ case USBDEVFS_DISCARDURB: { ++ GList *l = g_list_find_custom(fixture->flying_urbs, *(void**) ioctl_arg->data, cmp_ioctl_data_addr); ++ ++ if (l) { ++ fixture->discarded_urbs = g_list_append(fixture->discarded_urbs, l->data); ++ fixture->flying_urbs = g_list_delete_link(fixture->flying_urbs, l); ++ umockdev_ioctl_client_complete(client, 0, 0); ++ } else { ++ umockdev_ioctl_client_complete(client, -1, EINVAL); ++ } ++ ++ return TRUE; ++ } ++ ++ default: ++ return FALSE; ++ } ++} ++ ++static void ++test_fixture_add_canon(UMockdevTestbedFixture * fixture) ++{ ++ /* Setup first, so we can be sure libusb_open works when the add uevent ++ * happens. ++ */ ++ g_assert_cmpint(umockdev_testbed_attach_ioctl(fixture->testbed, "/dev/bus/usb/001/001", fixture->handler, NULL), ==, 1); ++ ++ /* NOTE: add_device would not create a file, needed for device emulation */ ++ /* XXX: Racy, see https://github.com/martinpitt/umockdev/issues/173 */ ++ umockdev_testbed_add_from_string(fixture->testbed, ++ "P: /devices/usb1\n" ++ "N: bus/usb/001/001\n" ++ "E: SUBSYSTEM=usb\n" ++ "E: DRIVER=usb\n" ++ "E: BUSNUM=001\n" ++ "E: DEVNUM=001\n" ++ "E: DEVNAME=/dev/bus/usb/001/001\n" ++ "E: DEVTYPE=usb_device\n" ++ "A: bConfigurationValue=1\\n\n" ++ "A: busnum=1\\n\n" ++ "A: devnum=1\\n\n" ++ "A: bConfigurationValue=1\\n\n" ++ "A: speed=480\\n\n" ++ /* descriptor from a Canon PowerShot SX200; VID 04a9 PID 31c0 */ ++ "H: descriptors=" ++ "1201000200000040a904c03102000102" ++ "030109022700010100c0010904000003" ++ "06010100070581020002000705020200" ++ "020007058303080009\n", ++ NULL); ++} ++ ++static void ++test_fixture_setup_libusb(UMockdevTestbedFixture * fixture, int devcount) ++{ ++ libusb_device **devs = NULL; ++ ++ libusb_init (&fixture->ctx); ++ ++ /* Supress global log messages completely ++ * (though, in some tests it might be interesting to check there are no real ones). ++ */ ++ libusb_set_log_cb (NULL, log_handler_null, LIBUSB_LOG_CB_GLOBAL); ++ libusb_set_option (fixture->ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG); ++ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, devcount); ++ libusb_free_device_list(devs, TRUE); ++ libusb_set_log_cb (fixture->ctx, log_handler, LIBUSB_LOG_CB_CONTEXT); ++} ++ ++static void ++test_fixture_setup_common(UMockdevTestbedFixture * fixture) ++{ ++ g_assert(cur_fixture == NULL); ++ cur_fixture = fixture; ++ ++ pthread_mutex_init(&fixture->mutex, NULL); ++ ++ fixture->testbed = umockdev_testbed_new(); ++ g_assert(fixture->testbed != NULL); ++ fixture->root_dir = umockdev_testbed_get_root_dir(fixture->testbed); ++ fixture->sys_dir = umockdev_testbed_get_sys_dir(fixture->testbed); ++ ++ fixture->handler = umockdev_ioctl_base_new(); ++ g_object_connect(fixture->handler, "signal-after::handle-ioctl", handle_ioctl_cb, fixture, NULL); ++} ++ ++#if 0 ++static void ++test_fixture_setup_empty(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ /* XXX: Racy, see https://github.com/martinpitt/umockdev/issues/173 */ ++ test_fixture_setup_common(fixture); ++ ++ test_fixture_setup_libusb(fixture, 0); ++} ++#endif ++ ++static void ++test_fixture_setup_with_canon(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ test_fixture_setup_common(fixture); ++ ++ test_fixture_add_canon(fixture); ++ ++ test_fixture_setup_libusb(fixture, 1); ++} ++ ++static void ++test_fixture_teardown(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ g_assert(cur_fixture == fixture); ++ ++ /* libusb_exit logs */ ++ if (fixture->ctx) ++ libusb_exit (fixture->ctx); ++ libusb_set_log_cb (NULL, NULL, LIBUSB_LOG_CB_GLOBAL); ++ cur_fixture = NULL; ++ ++ /* Abort if there are any warnings/errors in the log */ ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); ++ fixture->ctx = NULL; ++ g_assert_null(fixture->libusb_log); ++ ++ g_clear_object(&fixture->handler); ++ g_clear_object(&fixture->testbed); ++ ++ /* verify that temp dir gets cleaned up properly */ ++ g_assert(!g_file_test(fixture->root_dir, G_FILE_TEST_EXISTS)); ++ g_free(fixture->root_dir); ++ g_free(fixture->sys_dir); ++ ++ while (fixture->flying_urbs) { ++ umockdev_ioctl_data_unref (fixture->flying_urbs->data); ++ fixture->flying_urbs = g_list_delete_link (fixture->flying_urbs, fixture->flying_urbs); ++ } ++ ++ pthread_mutex_destroy(&fixture->mutex); ++} ++ ++static void ++test_open_close(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ libusb_device **devs = NULL; ++ struct libusb_device_descriptor desc; ++ libusb_device_handle *handle = NULL; ++ ++ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, 1); ++ /* The linux_enumerate_device may happen from a different thread */ ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_get_device_list"); ++ /* We have exactly one device */ ++ g_assert_cmpint(libusb_get_bus_number(devs[0]), ==, 1); ++ g_assert_cmpint(libusb_get_device_address(devs[0]), ==, 1); ++ ++ /* Get/Check descriptor */ ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); ++ libusb_get_device_descriptor (devs[0], &desc); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_get_device_descriptor"); ++ g_assert_cmpint(desc.idVendor, ==, 0x04a9); ++ g_assert_cmpint(desc.idProduct, ==, 0x31c0); ++ ++ /* Open and close */ ++ g_assert_cmpint(libusb_open(devs[0], &handle), ==, 0); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "usbi_add_event_source"); ++ g_assert_nonnull(handle); ++ libusb_close(handle); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "usbi_remove_event_source"); ++ ++ libusb_free_device_list(devs, TRUE); ++ ++ /* Open and close using vid/pid */ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ libusb_close(handle); ++} ++ ++static void ++test_close_flying(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ UsbChat chat[] = { ++ { ++ .submit = TRUE, ++ .type = USBDEVFS_URB_TYPE_BULK, ++ .endpoint = LIBUSB_ENDPOINT_OUT, ++ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, ++ .buffer_length = 4, ++ }, ++ { .submit = FALSE } ++ }; ++ libusb_device_handle *handle = NULL; ++ struct libusb_transfer *transfer = NULL; ++ ++ fixture->chat = chat; ++ ++ /* Open */ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ ++ transfer = libusb_alloc_transfer(0); ++ libusb_fill_bulk_transfer(transfer, ++ handle, ++ LIBUSB_ENDPOINT_OUT, ++ (unsigned char*) chat[0].buffer, ++ chat[0].buffer_length, ++ NULL, ++ NULL, ++ 1); ++ ++ /* Submit */ ++ libusb_submit_transfer(transfer); ++ ++ /* Closing logs fat error (two lines) */ ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); ++ libusb_close(handle); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*connected as far as we know"); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*cancellation hasn't even been scheduled"); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[do_close\\] Removed transfer"); ++ ++ /* Free'ing the transfer works, and logs to the right context */ ++ libusb_free_transfer(transfer); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[libusb_free_transfer\\]"); ++} ++ ++static void ++test_close_cancelled(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ UsbChat chat[] = { ++ { ++ .submit = TRUE, ++ .type = USBDEVFS_URB_TYPE_BULK, ++ .endpoint = LIBUSB_ENDPOINT_OUT, ++ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, ++ .buffer_length = 4, ++ }, ++ { .submit = FALSE } ++ }; ++ libusb_device_handle *handle = NULL; ++ struct libusb_transfer *transfer = NULL; ++ ++ fixture->chat = chat; ++ ++ /* Open */ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ ++ transfer = libusb_alloc_transfer(0); ++ libusb_fill_bulk_transfer(transfer, ++ handle, ++ LIBUSB_ENDPOINT_OUT, ++ (unsigned char*) chat[0].buffer, ++ chat[0].buffer_length, ++ NULL, ++ NULL, ++ 1); ++ ++ /* Submit */ ++ libusb_submit_transfer(transfer); ++ libusb_cancel_transfer(transfer); ++ ++ /* Closing logs fat error (two lines) */ ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); ++ libusb_close(handle); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*connected as far as we know"); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[do_close\\] .*cancellation.*hasn't completed"); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[do_close\\] Removed transfer"); ++ ++ libusb_free_transfer(transfer); ++} ++ ++static void ++test_ctx_destroy(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ UsbChat chat[] = { ++ { ++ .submit = TRUE, ++ .type = USBDEVFS_URB_TYPE_BULK, ++ .endpoint = LIBUSB_ENDPOINT_OUT, ++ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, ++ .buffer_length = 4, ++ }, ++ { .submit = FALSE } ++ }; ++ libusb_device_handle *handle = NULL; ++ struct libusb_transfer *transfer = NULL; ++ ++ fixture->chat = chat; ++ ++ /* Open */ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ ++ transfer = libusb_alloc_transfer(0); ++ libusb_fill_bulk_transfer(transfer, ++ handle, ++ LIBUSB_ENDPOINT_OUT, ++ (unsigned char*) chat[0].buffer, ++ chat[0].buffer_length, ++ NULL, ++ NULL, ++ 1); ++ ++ /* Submit */ ++ libusb_submit_transfer(transfer); ++ ++ /* Now we are evil and destroy the ctx! */ ++ libusb_exit(fixture->ctx); ++ ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[libusb_exit\\] device.*still referenced"); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[libusb_exit\\] application left some devices open"); ++ ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); ++ fixture->ctx = NULL; ++ ++ /* XXX: Closing crashes the application as it unref's the NULL pointer */ ++ /* libusb_close(handle); */ ++ ++ libusb_free_transfer(transfer); ++} ++ ++static void ++test_get_string_descriptor(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ unsigned char data[255] = { 0, }; ++ libusb_device_handle *handle = NULL; ++ UsbChat chat[] = { ++ { ++ .submit = TRUE, ++ .reaps = &chat[1], ++ .type = USBDEVFS_URB_TYPE_CONTROL, ++ .buffer_length = 12, /* 8 byte out*/ ++ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00", ++ }, { ++ /* String with content 0x0409 (en_US) */ ++ .reap = TRUE, ++ .actual_length = 12, ++ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00\x04\x03\x09\x04", ++ }, { ++ .submit = TRUE, ++ .reaps = &chat[3], ++ .type = USBDEVFS_URB_TYPE_CONTROL, ++ .buffer_length = 263, /* 8 byte out*/ ++ .buffer = (const unsigned char*) "\x80\x06\x01\x03\x09\x04\xff\x00", ++ }, { ++ /* 4 byte string, "ab" */ ++ .reap = TRUE, ++ .actual_length = 14, ++ .buffer = (const unsigned char*) "\x80\x06\x01\x03\x09\x04\xff\x00\x06\x03\x61\x00\x62\x00", ++ }, { ++ .submit = TRUE, ++ .reaps = &chat[5], ++ .type = USBDEVFS_URB_TYPE_CONTROL, ++ .buffer_length = 12, /* 8 byte out*/ ++ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00", ++ }, { ++ .reap = TRUE, ++ .status = -ENOENT, ++ }, { ++ .submit = TRUE, ++ .status = -ENOENT, ++ .type = USBDEVFS_URB_TYPE_CONTROL, ++ .buffer_length = 12, /* 8 byte out*/ ++ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00", ++ }, { ++ .submit = FALSE, ++ } ++ }; ++ ++ fixture->chat = chat; ++ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ ++ /* The chat allows us to fetch the descriptor */ ++ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, 2); ++ g_assert_cmpint(memcmp(data, "ab", 2), ==, 0); ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); ++ ++ /* Again, but the URB fails with ENOENT when reaping */ ++ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, -1); ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); ++ ++ /* Again, but the URB fails to submit with ENOENT */ ++ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, -1); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[submit_control_transfer\\] submiturb failed, errno=2"); ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); ++ ++ libusb_close(handle); ++} ++ ++static void ++transfer_cb_inc_user_data(struct libusb_transfer *transfer) ++{ ++ *(int*)transfer->user_data += 1; ++} ++ ++static void ++test_timeout(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ UsbChat chat[] = { ++ { ++ .submit = TRUE, ++ .type = USBDEVFS_URB_TYPE_BULK, ++ .endpoint = LIBUSB_ENDPOINT_OUT, ++ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, ++ .buffer_length = 4, ++ }, ++ { ++ .submit = FALSE, ++ } ++ }; ++ int completed = 0; ++ libusb_device_handle *handle = NULL; ++ struct libusb_transfer *transfer = NULL; ++ ++ fixture->chat = chat; ++ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ ++ transfer = libusb_alloc_transfer(0); ++ libusb_fill_bulk_transfer(transfer, ++ handle, ++ LIBUSB_ENDPOINT_OUT, ++ (unsigned char*) chat[0].buffer, ++ chat[0].buffer_length, ++ transfer_cb_inc_user_data, ++ &completed, ++ 10); ++ ++ libusb_submit_transfer(transfer); ++ while (!completed) { ++ g_assert_cmpint(libusb_handle_events_completed(fixture->ctx, &completed), ==, 0); ++ /* Silence after one iteration. */ ++ fixture->libusb_log_silence = TRUE; ++ } ++ fixture->libusb_log_silence = FALSE; ++ ++ g_assert_cmpint(transfer->status, ==, LIBUSB_TRANSFER_TIMED_OUT); ++ libusb_free_transfer(transfer); ++ ++ libusb_close(handle); ++} ++ ++#define THREADED_SUBMIT_URB_SETS 64 ++#define THREADED_SUBMIT_URB_IN_FLIGHT 64 ++typedef struct { ++ struct libusb_transfer *transfers[THREADED_SUBMIT_URB_IN_FLIGHT * THREADED_SUBMIT_URB_SETS]; ++ int submitted; ++ int completed; ++ int done; ++ UMockdevTestbedFixture *fixture; ++} TestThreadedSubmit; ++ ++static gpointer ++transfer_submit_all_retry(TestThreadedSubmit *data) ++{ ++ for (guint i = 0; i < G_N_ELEMENTS(data->transfers); i++) { ++ while (libusb_submit_transfer(data->transfers[i]) < 0) { ++ assert_libusb_log_msg(data->fixture, LIBUSB_LOG_LEVEL_ERROR, "submit_bulk_transfer"); ++ continue; ++ } ++ ++ data->submitted += 1; ++ } ++ ++ return NULL; ++} ++ ++static void ++test_threaded_submit_transfer_cb(struct libusb_transfer *transfer) ++{ ++ TestThreadedSubmit *data = transfer->user_data; ++ ++ /* We should only be receiving packets in the main thread */ ++ g_assert_cmpint (getpid(), ==, gettid()); ++ ++ /* Check that the transfer buffer has the expected value */ ++ g_assert_cmpint (*(int*)transfer->buffer, ==, data->completed); ++ data->completed += 1; ++ ++ if (data->completed == G_N_ELEMENTS(data->transfers)) ++ data->done = TRUE; ++} ++ ++static void ++test_threaded_submit(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ GThread *thread = NULL; ++ TestThreadedSubmit data = { .fixture = fixture }; ++ UsbChat out_msg = { ++ .submit = TRUE, ++ .type = USBDEVFS_URB_TYPE_BULK, ++ .endpoint = LIBUSB_ENDPOINT_IN, ++ .buffer_length = sizeof(int), ++ }; ++ UsbChat in_msg = { ++ .reap = TRUE, ++ .actual_length = 4, ++ }; ++ UsbChat *c; ++ libusb_device_handle *handle = NULL; ++ int urb; ++ ++ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); ++ g_assert_nonnull(handle); ++ ++ fixture->libusb_log_silence = TRUE; ++ ++ c = fixture->chat = g_new0(UsbChat, G_N_ELEMENTS(data.transfers) * 2 + 1); ++ urb = 0; ++ for (int i = 0; i < THREADED_SUBMIT_URB_SETS; i++) { ++ for (int j = 0; j < THREADED_SUBMIT_URB_IN_FLIGHT; j++) { ++ c[i*2*THREADED_SUBMIT_URB_IN_FLIGHT + j] = out_msg; ++ c[i*2*THREADED_SUBMIT_URB_IN_FLIGHT + j].reaps = &c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j]; ++ c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j] = in_msg; ++ c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j].buffer = (unsigned char*) g_new0(int, 1); ++ *(int*) c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j].buffer = urb; ++ ++ data.transfers[urb] = libusb_alloc_transfer(0); ++ libusb_fill_bulk_transfer(data.transfers[urb], ++ handle, ++ LIBUSB_ENDPOINT_IN, ++ g_malloc(out_msg.buffer_length), ++ out_msg.buffer_length, ++ test_threaded_submit_transfer_cb, ++ &data, ++ G_MAXUINT); ++ data.transfers[urb]->flags = LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; ++ urb++; ++ } ++ } ++ ++ thread = g_thread_new("transfer all", (GThreadFunc) transfer_submit_all_retry, &data); ++ ++ while (!data.done) ++ g_assert_cmpint(libusb_handle_events_completed(fixture->ctx, &data.done), ==, 0); ++ ++ g_thread_join(thread); ++ ++ fixture->libusb_log_silence = FALSE; ++ libusb_close(handle); ++ ++ for (int i = 0; i < 2 * THREADED_SUBMIT_URB_SETS * THREADED_SUBMIT_URB_SETS; i++) ++ g_clear_pointer ((void**) &c->buffer, g_free); ++ g_free (c); ++} ++ ++int ++main(int argc, char **argv) ++{ ++ g_test_init(&argc, &argv, NULL); ++ ++ g_test_add("/libusb/open-close", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_open_close, ++ test_fixture_teardown); ++ ++ g_test_add("/libusb/close-flying", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_close_flying, ++ test_fixture_teardown); ++ g_test_add("/libusb/close-cancelled", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_close_cancelled, ++ test_fixture_teardown); ++ ++ g_test_add("/libusb/ctx-destroy", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_ctx_destroy, ++ test_fixture_teardown); ++ ++ g_test_add("/libusb/string-descriptor", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_get_string_descriptor, ++ test_fixture_teardown); ++ ++ g_test_add("/libusb/timeout", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_timeout, ++ test_fixture_teardown); ++ ++ g_test_add("/libusb/threaded-submit", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_threaded_submit, ++ test_fixture_teardown); ++ ++ return g_test_run(); ++} +-- +2.35.1 + diff --git a/libusb1.spec b/libusb1.spec index 901fbd2..0602138 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -6,6 +6,7 @@ Source0: https://github.com/libusb/libusb/releases/download/v%{version}/l License: LGPLv2+ URL: http://libusb.info BuildRequires: systemd-devel doxygen libtool +BuildRequires: umockdev-devel BuildRequires: make BuildRequires: gcc # libusbx was removed in F34 @@ -17,6 +18,8 @@ Obsoletes: libusbx < %{version}-%{release} Patch0001: https://github.com/libusb/libusb/pull/1058.patch # Fix a crash if a transfer outlives closing the device Patch0002: https://github.com/libusb/libusb/pull/1073.patch +# Add umockdev based tests from https://github.com/libusb/libusb/pull/1078 +Patch0003: 0001-tests-Add-some-umockdev-based-tests.patch %description This package provides a way for applications to access USB devices. @@ -82,6 +85,7 @@ popd %{make_install} mkdir -p $RPM_BUILD_ROOT%{_bindir} install -m 755 tests/.libs/stress $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress +install -m 755 tests/.libs/umockdev $RPM_BUILD_ROOT%{_bindir}/libusb-test-umockdev install -m 755 examples/.libs/testlibusb \ $RPM_BUILD_ROOT%{_bindir}/libusb-test-libusb # Some examples are very device-specific / require specific hw and miss --help @@ -96,6 +100,7 @@ rm $RPM_BUILD_ROOT%{_libdir}/*.la %check LD_LIBRARY_PATH=libusb/.libs ldd $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress +LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-umockdev LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-libusb LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-example-listdevs @@ -121,6 +126,7 @@ LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-example-listdevs %{_bindir}/libusb-example-listdevs %{_bindir}/libusb-example-xusb %{_bindir}/libusb-test-stress +%{_bindir}/libusb-test-umockdev %{_bindir}/libusb-test-libusb diff --git a/tests/run.sh b/tests/run.sh index 440092c..36c9ae3 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -4,5 +4,6 @@ set -e ldd /usr/bin/libusb-test-stress /usr/bin/libusb-test-stress +/usr/bin/libusb-test-umockdev /usr/bin/libusb-test-libusb /usr/bin/libusb-example-listdevs From 5e2dcc2c62b6603c3db85a783a09cb82af79499e Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 22 Feb 2022 12:06:52 +0100 Subject: [PATCH 15/42] Work around API misuse in gutenprint Related: #2056326 --- ...ll-first-context-as-implicit-default.patch | 158 ++++++++++++++++++ libusb1.spec | 4 + 2 files changed, 162 insertions(+) create mode 100644 0001-core-Install-first-context-as-implicit-default.patch diff --git a/0001-core-Install-first-context-as-implicit-default.patch b/0001-core-Install-first-context-as-implicit-default.patch new file mode 100644 index 0000000..b64a29e --- /dev/null +++ b/0001-core-Install-first-context-as-implicit-default.patch @@ -0,0 +1,158 @@ +From d46cbbac4851ce6e49d8dacb0daa328453eb8a84 Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Tue, 22 Feb 2022 11:45:38 +0100 +Subject: [PATCH] core: Install first context as implicit default + +There was a behaviour change in libusb, which triggers issues when the +API is misused. This caused gutenprint to crash, see +https://bugzilla.redhat.com/show_bug.cgi?id=2055504 + +For now, work around this by installing an implicit default. But, change +the code to log an error in case this "feature" is being used. +--- + libusb/core.c | 16 +++++++++++++--- + libusb/libusbi.h | 15 ++++++++++++++- + tests/umockdev.c | 31 +++++++++++++++++++++++++++++++ + 3 files changed, 58 insertions(+), 4 deletions(-) + +diff --git a/libusb/core.c b/libusb/core.c +index 1c1ada1..c75ddae 100644 +--- a/libusb/core.c ++++ b/libusb/core.c +@@ -41,6 +41,7 @@ static libusb_log_cb log_handler; + #endif + + struct libusb_context *usbi_default_context; ++struct libusb_context *usbi_fallback_context; + static int default_context_refcnt; + static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; + static struct usbi_option default_context_options[LIBUSB_OPTION_MAX]; +@@ -2284,7 +2285,7 @@ int API_EXPORTED libusb_init(libusb_context **ctx) + + usbi_mutex_static_lock(&default_context_lock); + +- if (!ctx && usbi_default_context) { ++ if (!ctx && default_context_refcnt > 0) { + usbi_dbg(usbi_default_context, "reusing default context"); + default_context_refcnt++; + usbi_mutex_static_unlock(&default_context_lock); +@@ -2354,9 +2355,15 @@ int API_EXPORTED libusb_init(libusb_context **ctx) + goto err_io_exit; + } + +- if (ctx) ++ if (ctx) { + *ctx = _ctx; + ++ if (!usbi_fallback_context) { ++ usbi_fallback_context = _ctx; ++ usbi_warn(usbi_fallback_context, "installing new context as implicit default"); ++ } ++ } ++ + usbi_mutex_static_unlock(&default_context_lock); + + return 0; +@@ -2429,6 +2436,8 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) + + if (!ctx) + usbi_default_context = NULL; ++ if (ctx == usbi_fallback_context) ++ usbi_fallback_context = NULL; + + usbi_mutex_static_unlock(&default_context_lock); + +@@ -2575,7 +2584,8 @@ static void log_v(struct libusb_context *ctx, enum libusb_log_level level, + #else + enum libusb_log_level ctx_level; + +- ctx = usbi_get_context(ctx); ++ ctx = ctx ? ctx : usbi_default_context; ++ ctx = ctx ? ctx : usbi_fallback_context; + if (ctx) + ctx_level = ctx->debug; + else +diff --git a/libusb/libusbi.h b/libusb/libusbi.h +index 5f0d5c2..580add8 100644 +--- a/libusb/libusbi.h ++++ b/libusb/libusbi.h +@@ -436,13 +436,26 @@ struct libusb_context { + }; + + extern struct libusb_context *usbi_default_context; ++extern struct libusb_context *usbi_fallback_context; + + extern struct list_head active_contexts_list; + extern usbi_mutex_static_t active_contexts_lock; + + static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx) + { +- return ctx ? ctx : usbi_default_context; ++ static int warned = 0; ++ ++ if (!ctx) { ++ ctx = usbi_default_context; ++ } ++ if (!ctx) { ++ ctx = usbi_fallback_context; ++ if (ctx && warned == 0) { ++ usbi_err(ctx, "API misuse! Using non-default context as implicit default."); ++ warned = 1; ++ } ++ } ++ return ctx; + } + + enum usbi_event_flags { +diff --git a/tests/umockdev.c b/tests/umockdev.c +index b2af512..0e73f94 100644 +--- a/tests/umockdev.c ++++ b/tests/umockdev.c +@@ -551,6 +551,32 @@ test_open_close(UMockdevTestbedFixture * fixture, UNUSED_DATA) + libusb_close(handle); + } + ++static void ++test_implicit_default(UMockdevTestbedFixture * fixture, UNUSED_DATA) ++{ ++ libusb_device **devs = NULL; ++ ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); ++ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); ++ libusb_free_device_list(devs, TRUE); ++ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[usbi_get_context\\].*implicit default"); ++ ++ /* Only warns once */ ++ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); ++ libusb_free_device_list(devs, TRUE); ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); ++ ++ libusb_init(NULL); ++ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); ++ libusb_exit(NULL); ++ ++ /* We free late, causing a warning from libusb_exit. However, ++ * we never see this warning (i.e. test success) because it is on a ++ * different context. ++ */ ++ libusb_free_device_list(devs, TRUE); ++} ++ + static void + test_close_flying(UMockdevTestbedFixture * fixture, UNUSED_DATA) + { +@@ -932,6 +958,11 @@ main(int argc, char **argv) + test_open_close, + test_fixture_teardown); + ++ g_test_add("/libusb/implicit-default", UMockdevTestbedFixture, NULL, ++ test_fixture_setup_with_canon, ++ test_implicit_default, ++ test_fixture_teardown); ++ + g_test_add("/libusb/close-flying", UMockdevTestbedFixture, NULL, + test_fixture_setup_with_canon, + test_close_flying, +-- +2.35.1 + diff --git a/libusb1.spec b/libusb1.spec index 0602138..e7c8582 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -21,6 +21,10 @@ Patch0002: https://github.com/libusb/libusb/pull/1073.patch # Add umockdev based tests from https://github.com/libusb/libusb/pull/1078 Patch0003: 0001-tests-Add-some-umockdev-based-tests.patch +# Work around API misuse in gutenprint +# https://bugzilla.redhat.com/show_bug.cgi?id=2056326 +Patch9999: 0001-core-Install-first-context-as-implicit-default.patch + %description This package provides a way for applications to access USB devices. From 6352bb192a0aea9cdf1dcede0ae1568aa1b1c06b Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 22 Feb 2022 13:08:24 +0100 Subject: [PATCH 16/42] Enable and fix gating tests --- gating.yaml | 8 +++++++- tests/tests.yml | 6 +----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/gating.yaml b/gating.yaml index d71584a..f2c6454 100644 --- a/gating.yaml +++ b/gating.yaml @@ -3,4 +3,10 @@ product_versions: - fedora-* decision_context: bodhi_update_push_testing rules: - - !PassingTestCaseRule {test_case_name: dist.depcheck} + - !PassingTestCaseRule {test_case_name: fedora-ci.koji-build.tier0.functional} +--- !Policy +product_versions: + - fedora-* +decision_context: bodhi_update_push_stable +rules: + - !PassingTestCaseRule {test_case_name: fedora-ci.koji-build.tier0.functional} diff --git a/tests/tests.yml b/tests/tests.yml index 82f0be7..57b120c 100644 --- a/tests/tests.yml +++ b/tests/tests.yml @@ -1,11 +1,7 @@ --- - hosts: localhost roles: - - role: standard-test-source - tags: - - always - required_packages: - - git + # Running these tests does not require the sources - role: standard-test-basic tags: - atomic From ff9cb1c8b60c112ce2a827d4249e2e30ccb5525a Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 28 Feb 2022 14:17:41 +0100 Subject: [PATCH 17/42] Update patches and fix spec file The previous update contained old versions of the patches and update. Update the patches/fixes and spec file accordingly. --- ...ll-first-context-as-implicit-default.patch | 113 ++++++++---------- ...-tests-Add-some-umockdev-based-tests.patch | 20 ++-- libusb1.spec | 2 +- tests/run.sh | 1 + tests/tests.yml | 6 +- 5 files changed, 62 insertions(+), 80 deletions(-) diff --git a/0001-core-Install-first-context-as-implicit-default.patch b/0001-core-Install-first-context-as-implicit-default.patch index b04207b..b64a29e 100644 --- a/0001-core-Install-first-context-as-implicit-default.patch +++ b/0001-core-Install-first-context-as-implicit-default.patch @@ -1,4 +1,4 @@ -From f74f1e21b6201ec803347ddaf2737cdaf9a1180a Mon Sep 17 00:00:00 2001 +From d46cbbac4851ce6e49d8dacb0daa328453eb8a84 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Tue, 22 Feb 2022 11:45:38 +0100 Subject: [PATCH] core: Install first context as implicit default @@ -10,106 +10,77 @@ https://bugzilla.redhat.com/show_bug.cgi?id=2055504 For now, work around this by installing an implicit default. But, change the code to log an error in case this "feature" is being used. --- - libusb/core.c | 24 ++++++++++++++++-------- - libusb/libusbi.h | 8 +++++++- - tests/umockdev.c | 26 ++++++++++++++++++++++++++ - 3 files changed, 49 insertions(+), 9 deletions(-) + libusb/core.c | 16 +++++++++++++--- + libusb/libusbi.h | 15 ++++++++++++++- + tests/umockdev.c | 31 +++++++++++++++++++++++++++++++ + 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/libusb/core.c b/libusb/core.c -index 1c1ada1..c56df72 100644 +index 1c1ada1..c75ddae 100644 --- a/libusb/core.c +++ b/libusb/core.c -@@ -41,7 +41,7 @@ static libusb_log_cb log_handler; +@@ -41,6 +41,7 @@ static libusb_log_cb log_handler; #endif struct libusb_context *usbi_default_context; --static int default_context_refcnt; -+int usbi_default_context_refcnt; ++struct libusb_context *usbi_fallback_context; + static int default_context_refcnt; static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; static struct usbi_option default_context_options[LIBUSB_OPTION_MAX]; - -@@ -2284,9 +2284,9 @@ int API_EXPORTED libusb_init(libusb_context **ctx) +@@ -2284,7 +2285,7 @@ int API_EXPORTED libusb_init(libusb_context **ctx) usbi_mutex_static_lock(&default_context_lock); - if (!ctx && usbi_default_context) { -+ if (!ctx && usbi_default_context_refcnt > 0) { ++ if (!ctx && default_context_refcnt > 0) { usbi_dbg(usbi_default_context, "reusing default context"); -- default_context_refcnt++; -+ usbi_default_context_refcnt++; + default_context_refcnt++; usbi_mutex_static_unlock(&default_context_lock); - return 0; - } -@@ -2331,7 +2331,7 @@ int API_EXPORTED libusb_init(libusb_context **ctx) - /* default context must be initialized before calling usbi_dbg */ - if (!ctx) { - usbi_default_context = _ctx; -- default_context_refcnt = 1; -+ usbi_default_context_refcnt = 1; - usbi_dbg(usbi_default_context, "created default context"); +@@ -2354,9 +2355,15 @@ int API_EXPORTED libusb_init(libusb_context **ctx) + goto err_io_exit; } -@@ -2357,6 +2357,12 @@ int API_EXPORTED libusb_init(libusb_context **ctx) - if (ctx) +- if (ctx) ++ if (ctx) { *ctx = _ctx; -+ if (usbi_default_context == NULL) { -+ usbi_default_context = _ctx; -+ usbi_default_context_refcnt = 0; -+ usbi_dbg(usbi_default_context, "installing new context as implicit default"); ++ if (!usbi_fallback_context) { ++ usbi_fallback_context = _ctx; ++ usbi_warn(usbi_fallback_context, "installing new context as implicit default"); ++ } + } + usbi_mutex_static_unlock(&default_context_lock); return 0; -@@ -2373,7 +2379,7 @@ err_free_ctx: - if (!ctx) { - /* clear default context that was not fully initialized */ +@@ -2429,6 +2436,8 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) + + if (!ctx) usbi_default_context = NULL; -- default_context_refcnt = 0; -+ usbi_default_context_refcnt = 0; - } - - usbi_mutex_destroy(&_ctx->open_devs_lock); -@@ -2407,7 +2413,7 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) - return; - } - -- if (--default_context_refcnt > 0) { -+ if (--usbi_default_context_refcnt > 0) { - usbi_dbg(ctx, "not destroying default context"); - usbi_mutex_static_unlock(&default_context_lock); - return; -@@ -2427,8 +2433,10 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) - if (usbi_backend.exit) - usbi_backend.exit(_ctx); - -- if (!ctx) -+ if (!ctx || ctx == usbi_default_context) { - usbi_default_context = NULL; -+ assert(usbi_default_context_refcnt == 0); -+ } ++ if (ctx == usbi_fallback_context) ++ usbi_fallback_context = NULL; usbi_mutex_static_unlock(&default_context_lock); -@@ -2575,7 +2583,7 @@ static void log_v(struct libusb_context *ctx, enum libusb_log_level level, +@@ -2575,7 +2584,8 @@ static void log_v(struct libusb_context *ctx, enum libusb_log_level level, #else enum libusb_log_level ctx_level; - ctx = usbi_get_context(ctx); + ctx = ctx ? ctx : usbi_default_context; ++ ctx = ctx ? ctx : usbi_fallback_context; if (ctx) ctx_level = ctx->debug; else diff --git a/libusb/libusbi.h b/libusb/libusbi.h -index 5f0d5c2..a404cff 100644 +index 5f0d5c2..580add8 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h -@@ -436,13 +436,19 @@ struct libusb_context { +@@ -436,13 +436,26 @@ struct libusb_context { }; extern struct libusb_context *usbi_default_context; -+extern int usbi_default_context_refcnt; ++extern struct libusb_context *usbi_fallback_context; extern struct list_head active_contexts_list; extern usbi_mutex_static_t active_contexts_lock; @@ -117,20 +88,27 @@ index 5f0d5c2..a404cff 100644 static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx) { - return ctx ? ctx : usbi_default_context; -+ if (!ctx && usbi_default_context) { -+ if (usbi_default_context_refcnt == 0) -+ usbi_err(usbi_default_context, "API misuse! Using non-default context as implicit default."); ++ static int warned = 0; ++ ++ if (!ctx) { + ctx = usbi_default_context; + } ++ if (!ctx) { ++ ctx = usbi_fallback_context; ++ if (ctx && warned == 0) { ++ usbi_err(ctx, "API misuse! Using non-default context as implicit default."); ++ warned = 1; ++ } ++ } + return ctx; } enum usbi_event_flags { diff --git a/tests/umockdev.c b/tests/umockdev.c -index b2af512..7de0aae 100644 +index b2af512..0e73f94 100644 --- a/tests/umockdev.c +++ b/tests/umockdev.c -@@ -551,6 +551,27 @@ test_open_close(UMockdevTestbedFixture * fixture, UNUSED_DATA) +@@ -551,6 +551,32 @@ test_open_close(UMockdevTestbedFixture * fixture, UNUSED_DATA) libusb_close(handle); } @@ -144,6 +122,11 @@ index b2af512..7de0aae 100644 + libusb_free_device_list(devs, TRUE); + assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[usbi_get_context\\].*implicit default"); + ++ /* Only warns once */ ++ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); ++ libusb_free_device_list(devs, TRUE); ++ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); ++ + libusb_init(NULL); + g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); + libusb_exit(NULL); @@ -158,7 +141,7 @@ index b2af512..7de0aae 100644 static void test_close_flying(UMockdevTestbedFixture * fixture, UNUSED_DATA) { -@@ -932,6 +953,11 @@ main(int argc, char **argv) +@@ -932,6 +958,11 @@ main(int argc, char **argv) test_open_close, test_fixture_teardown); diff --git a/0001-tests-Add-some-umockdev-based-tests.patch b/0001-tests-Add-some-umockdev-based-tests.patch index 204879f..8aba02d 100644 --- a/0001-tests-Add-some-umockdev-based-tests.patch +++ b/0001-tests-Add-some-umockdev-based-tests.patch @@ -1,7 +1,7 @@ -From 4f413d618311edf422b1b8c7ddb05919517f151d Mon Sep 17 00:00:00 2001 +From 87976d513fbb35e13cbcd8cb00d2abc1cac0dd54 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 18 Feb 2022 16:26:29 +0100 -Subject: [PATCH 1/3] tests: Add some umockdev based tests +Subject: [PATCH 1/2] tests: Add some umockdev based tests Add some basic umockdev tests. The setup in this case is all in one process, but umockdev in principle could also work between processes. @@ -9,9 +9,9 @@ process, but umockdev in principle could also work between processes. This is just a start, more tests would make sense. --- configure.ac | 7 + - tests/Makefile.am | 11 +- + tests/Makefile.am | 13 +- tests/umockdev.c | 965 ++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 982 insertions(+), 1 deletion(-) + 3 files changed, 984 insertions(+), 1 deletion(-) create mode 100644 tests/umockdev.c diff --git a/configure.ac b/configure.ac @@ -40,10 +40,10 @@ index f6cf2f9..5336463 100644 AM_CONDITIONAL([OS_DARWIN], [test "x$backend" = xdarwin]) AM_CONDITIONAL([OS_HAIKU], [test "x$backend" = xhaiku]) diff --git a/tests/Makefile.am b/tests/Makefile.am -index cb8fad9..ace44bf 100644 +index cb8fad9..cf6237e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am -@@ -2,6 +2,15 @@ AM_CPPFLAGS = -I$(top_srcdir)/libusb +@@ -2,6 +2,17 @@ AM_CPPFLAGS = -I$(top_srcdir)/libusb LDADD = ../libusb/libusb-1.0.la LIBS = @@ -54,15 +54,17 @@ index cb8fad9..ace44bf 100644 -stress_SOURCES = stress.c libusb_testlib.h testlib.c +if BUILD_UMOCKDEV_TEST +# NOTE: We add libumockdev-preload.so so that we can run tests in-process ++# We also use -Wl,-lxxx as the compiler doesn't need it and libtool ++# would reorder the flags otherwise. +umockdev_CPPFLAGS = ${UMOCKDEV_CFLAGS} -I$(top_srcdir)/libusb -+umockdev_LDFLAGS = -Wl,--no-as-needed -lumockdev-preload ${UMOCKDEV_LIBS} ++umockdev_LDFLAGS = -Wl,--push-state,--no-as-needed -Wl,-lumockdev-preload -Wl,--pop-state ${UMOCKDEV_LIBS} +umockdev_SOURCES = umockdev.c + +noinst_PROGRAMS += umockdev +endif diff --git a/tests/umockdev.c b/tests/umockdev.c new file mode 100644 -index 0000000..b2af512 +index 0000000..c026b51 --- /dev/null +++ b/tests/umockdev.c @@ -0,0 +1,965 @@ @@ -986,7 +988,7 @@ index 0000000..b2af512 + libusb_close(handle); + + for (int i = 0; i < 2 * THREADED_SUBMIT_URB_SETS * THREADED_SUBMIT_URB_SETS; i++) -+ g_clear_pointer ((char**) &c->buffer, g_free); ++ g_clear_pointer ((void**) &c->buffer, g_free); + g_free (c); +} + diff --git a/libusb1.spec b/libusb1.spec index a97ff41..e7c8582 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -6,7 +6,7 @@ Source0: https://github.com/libusb/libusb/releases/download/v%{version}/l License: LGPLv2+ URL: http://libusb.info BuildRequires: systemd-devel doxygen libtool -BuildRequiers: umockdev +BuildRequires: umockdev-devel BuildRequires: make BuildRequires: gcc # libusbx was removed in F34 diff --git a/tests/run.sh b/tests/run.sh index 440092c..36c9ae3 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -4,5 +4,6 @@ set -e ldd /usr/bin/libusb-test-stress /usr/bin/libusb-test-stress +/usr/bin/libusb-test-umockdev /usr/bin/libusb-test-libusb /usr/bin/libusb-example-listdevs diff --git a/tests/tests.yml b/tests/tests.yml index 82f0be7..57b120c 100644 --- a/tests/tests.yml +++ b/tests/tests.yml @@ -1,11 +1,7 @@ --- - hosts: localhost roles: - - role: standard-test-source - tags: - - always - required_packages: - - git + # Running these tests does not require the sources - role: standard-test-basic tags: - atomic From 9568c3f303cea6323d67402ec9affd668c6df08c Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 28 Feb 2022 18:51:48 +0100 Subject: [PATCH 18/42] Fix changelog and release version Some revisions had a broken .spec file, work around that issue. --- changelog | 24 ++++++++++++++++++++++++ libusb1.spec | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/changelog b/changelog index 293a1de..c2ace25 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,27 @@ +* Mon Feb 28 2022 Benjamin Berg 1.0.25-8 +- Fix changelog and release version + +* Mon Feb 28 2022 Benjamin Berg 1.0.25-7 +- Update patches and fix spec file + +* Tue Feb 22 2022 Benjamin Berg 1.0.25-6 +- Work around API misuse in gutenprint + +* Tue Feb 22 2022 Benjamin Berg 1.0.25-5 +- Add umockdev based tests + +* Tue Feb 22 2022 Benjamin Berg 1.0.25-4 +- Update regression fix patch for late transfer free's + +* Tue Feb 15 2022 Benjamin Berg 1.0.25-3 +- Fix a crash if a transfer outlives closing the device + +* Thu Feb 10 2022 Benjamin Berg 1.0.25-2 +- Fix a crash after libusb_exit API has been misused + +* Wed Feb 02 2022 Benjamin Berg 1.0.25-1 +- Update to 1.0.25 + * Thu Jul 22 2021 Fedora Release Engineering - 1.0.24-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild diff --git a/libusb1.spec b/libusb1.spec index e7c8582..68d3bb1 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -1,7 +1,7 @@ Summary: Library for accessing USB devices Name: libusb1 Version: 1.0.25 -Release: %autorelease +Release: %autorelease -b 7 Source0: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2 License: LGPLv2+ URL: http://libusb.info From b6b18ebcd94fd4da20959d46d6a965755238fb34 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 21 Jul 2022 19:17:58 +0000 Subject: [PATCH 19/42] Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild Signed-off-by: Fedora Release Engineering From 91d4c17a0b478d7823aafc67be0d09610a8c3f13 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Fri, 30 Sep 2022 16:24:59 +0800 Subject: [PATCH 20/42] Update to 1.0.26 --- .gitignore | 1 + ...ll-first-context-as-implicit-default.patch | 158 --- ...-tests-Add-some-umockdev-based-tests.patch | 1038 ----------------- 1058.patch | 24 - 1073.patch | 133 --- changelog | 3 + libusb1.spec | 14 +- sources | 2 +- 8 files changed, 6 insertions(+), 1367 deletions(-) delete mode 100644 0001-core-Install-first-context-as-implicit-default.patch delete mode 100644 0001-tests-Add-some-umockdev-based-tests.patch delete mode 100644 1058.patch delete mode 100644 1073.patch diff --git a/.gitignore b/.gitignore index 3a9ae78..255da81 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /libusb-1.0.24.tar.bz2 /libusb-1.0.25.tar.bz2 +/libusb-1.0.26.tar.bz2 diff --git a/0001-core-Install-first-context-as-implicit-default.patch b/0001-core-Install-first-context-as-implicit-default.patch deleted file mode 100644 index b64a29e..0000000 --- a/0001-core-Install-first-context-as-implicit-default.patch +++ /dev/null @@ -1,158 +0,0 @@ -From d46cbbac4851ce6e49d8dacb0daa328453eb8a84 Mon Sep 17 00:00:00 2001 -From: Benjamin Berg -Date: Tue, 22 Feb 2022 11:45:38 +0100 -Subject: [PATCH] core: Install first context as implicit default - -There was a behaviour change in libusb, which triggers issues when the -API is misused. This caused gutenprint to crash, see -https://bugzilla.redhat.com/show_bug.cgi?id=2055504 - -For now, work around this by installing an implicit default. But, change -the code to log an error in case this "feature" is being used. ---- - libusb/core.c | 16 +++++++++++++--- - libusb/libusbi.h | 15 ++++++++++++++- - tests/umockdev.c | 31 +++++++++++++++++++++++++++++++ - 3 files changed, 58 insertions(+), 4 deletions(-) - -diff --git a/libusb/core.c b/libusb/core.c -index 1c1ada1..c75ddae 100644 ---- a/libusb/core.c -+++ b/libusb/core.c -@@ -41,6 +41,7 @@ static libusb_log_cb log_handler; - #endif - - struct libusb_context *usbi_default_context; -+struct libusb_context *usbi_fallback_context; - static int default_context_refcnt; - static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; - static struct usbi_option default_context_options[LIBUSB_OPTION_MAX]; -@@ -2284,7 +2285,7 @@ int API_EXPORTED libusb_init(libusb_context **ctx) - - usbi_mutex_static_lock(&default_context_lock); - -- if (!ctx && usbi_default_context) { -+ if (!ctx && default_context_refcnt > 0) { - usbi_dbg(usbi_default_context, "reusing default context"); - default_context_refcnt++; - usbi_mutex_static_unlock(&default_context_lock); -@@ -2354,9 +2355,15 @@ int API_EXPORTED libusb_init(libusb_context **ctx) - goto err_io_exit; - } - -- if (ctx) -+ if (ctx) { - *ctx = _ctx; - -+ if (!usbi_fallback_context) { -+ usbi_fallback_context = _ctx; -+ usbi_warn(usbi_fallback_context, "installing new context as implicit default"); -+ } -+ } -+ - usbi_mutex_static_unlock(&default_context_lock); - - return 0; -@@ -2429,6 +2436,8 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) - - if (!ctx) - usbi_default_context = NULL; -+ if (ctx == usbi_fallback_context) -+ usbi_fallback_context = NULL; - - usbi_mutex_static_unlock(&default_context_lock); - -@@ -2575,7 +2584,8 @@ static void log_v(struct libusb_context *ctx, enum libusb_log_level level, - #else - enum libusb_log_level ctx_level; - -- ctx = usbi_get_context(ctx); -+ ctx = ctx ? ctx : usbi_default_context; -+ ctx = ctx ? ctx : usbi_fallback_context; - if (ctx) - ctx_level = ctx->debug; - else -diff --git a/libusb/libusbi.h b/libusb/libusbi.h -index 5f0d5c2..580add8 100644 ---- a/libusb/libusbi.h -+++ b/libusb/libusbi.h -@@ -436,13 +436,26 @@ struct libusb_context { - }; - - extern struct libusb_context *usbi_default_context; -+extern struct libusb_context *usbi_fallback_context; - - extern struct list_head active_contexts_list; - extern usbi_mutex_static_t active_contexts_lock; - - static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx) - { -- return ctx ? ctx : usbi_default_context; -+ static int warned = 0; -+ -+ if (!ctx) { -+ ctx = usbi_default_context; -+ } -+ if (!ctx) { -+ ctx = usbi_fallback_context; -+ if (ctx && warned == 0) { -+ usbi_err(ctx, "API misuse! Using non-default context as implicit default."); -+ warned = 1; -+ } -+ } -+ return ctx; - } - - enum usbi_event_flags { -diff --git a/tests/umockdev.c b/tests/umockdev.c -index b2af512..0e73f94 100644 ---- a/tests/umockdev.c -+++ b/tests/umockdev.c -@@ -551,6 +551,32 @@ test_open_close(UMockdevTestbedFixture * fixture, UNUSED_DATA) - libusb_close(handle); - } - -+static void -+test_implicit_default(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ libusb_device **devs = NULL; -+ -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); -+ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); -+ libusb_free_device_list(devs, TRUE); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[usbi_get_context\\].*implicit default"); -+ -+ /* Only warns once */ -+ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); -+ libusb_free_device_list(devs, TRUE); -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); -+ -+ libusb_init(NULL); -+ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); -+ libusb_exit(NULL); -+ -+ /* We free late, causing a warning from libusb_exit. However, -+ * we never see this warning (i.e. test success) because it is on a -+ * different context. -+ */ -+ libusb_free_device_list(devs, TRUE); -+} -+ - static void - test_close_flying(UMockdevTestbedFixture * fixture, UNUSED_DATA) - { -@@ -932,6 +958,11 @@ main(int argc, char **argv) - test_open_close, - test_fixture_teardown); - -+ g_test_add("/libusb/implicit-default", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_implicit_default, -+ test_fixture_teardown); -+ - g_test_add("/libusb/close-flying", UMockdevTestbedFixture, NULL, - test_fixture_setup_with_canon, - test_close_flying, --- -2.35.1 - diff --git a/0001-tests-Add-some-umockdev-based-tests.patch b/0001-tests-Add-some-umockdev-based-tests.patch deleted file mode 100644 index 8aba02d..0000000 --- a/0001-tests-Add-some-umockdev-based-tests.patch +++ /dev/null @@ -1,1038 +0,0 @@ -From 87976d513fbb35e13cbcd8cb00d2abc1cac0dd54 Mon Sep 17 00:00:00 2001 -From: Benjamin Berg -Date: Fri, 18 Feb 2022 16:26:29 +0100 -Subject: [PATCH 1/2] tests: Add some umockdev based tests - -Add some basic umockdev tests. The setup in this case is all in one -process, but umockdev in principle could also work between processes. - -This is just a start, more tests would make sense. ---- - configure.ac | 7 + - tests/Makefile.am | 13 +- - tests/umockdev.c | 965 ++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 984 insertions(+), 1 deletion(-) - create mode 100644 tests/umockdev.c - -diff --git a/configure.ac b/configure.ac -index f6cf2f9..5336463 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -177,6 +177,12 @@ linux) - dnl system has udev. use it or fail! - AC_CHECK_HEADER([libudev.h], [], [AC_MSG_ERROR([udev support requested but libudev header not installed])]) - AC_CHECK_LIB([udev], [udev_new], [], [AC_MSG_ERROR([udev support requested but libudev not installed])]) -+ -+ # We can build umockdev tests (if available) -+ PKG_PROG_PKG_CONFIG -+ PKG_CHECK_MODULES(UMOCKDEV, umockdev-1.0 >= 0.16.0, ac_have_umockdev=yes, ac_have_umockdev=no) -+ AC_SUBST(UMOCKDEV_CFLAGS) -+ AC_SUBST(UMOCKDEV_LIBS) - else - AC_CHECK_HEADERS([asm/types.h]) - AC_CHECK_HEADER([linux/netlink.h], [], [AC_MSG_ERROR([Linux netlink header not found])]) -@@ -356,6 +362,7 @@ AC_ARG_ENABLE([tests-build], - - AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" != xno]) - AM_CONDITIONAL([BUILD_TESTS], [test "x$build_tests" != xno]) -+AM_CONDITIONAL([BUILD_UMOCKDEV_TEST], [test "x$ac_have_umockdev" = xyes -a "x$log_enabled" != xno]) - AM_CONDITIONAL([CREATE_IMPORT_LIB], [test "x$create_import_lib" = xyes]) - AM_CONDITIONAL([OS_DARWIN], [test "x$backend" = xdarwin]) - AM_CONDITIONAL([OS_HAIKU], [test "x$backend" = xhaiku]) -diff --git a/tests/Makefile.am b/tests/Makefile.am -index cb8fad9..cf6237e 100644 ---- a/tests/Makefile.am -+++ b/tests/Makefile.am -@@ -2,6 +2,17 @@ AM_CPPFLAGS = -I$(top_srcdir)/libusb - LDADD = ../libusb/libusb-1.0.la - LIBS = - -+stress_SOURCES = stress.c libusb_testlib.h testlib.c -+ - noinst_PROGRAMS = stress - --stress_SOURCES = stress.c libusb_testlib.h testlib.c -+if BUILD_UMOCKDEV_TEST -+# NOTE: We add libumockdev-preload.so so that we can run tests in-process -+# We also use -Wl,-lxxx as the compiler doesn't need it and libtool -+# would reorder the flags otherwise. -+umockdev_CPPFLAGS = ${UMOCKDEV_CFLAGS} -I$(top_srcdir)/libusb -+umockdev_LDFLAGS = -Wl,--push-state,--no-as-needed -Wl,-lumockdev-preload -Wl,--pop-state ${UMOCKDEV_LIBS} -+umockdev_SOURCES = umockdev.c -+ -+noinst_PROGRAMS += umockdev -+endif -diff --git a/tests/umockdev.c b/tests/umockdev.c -new file mode 100644 -index 0000000..c026b51 ---- /dev/null -+++ b/tests/umockdev.c -@@ -0,0 +1,965 @@ -+/* -+ * libusb umockdev based tests -+ * -+ * Copyright (C) 2022 Benjamin Berg -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public License -+ * along with this program; If not, see . -+ */ -+ -+#define _GNU_SOURCE -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "libusb.h" -+ -+#include "umockdev.h" -+ -+#define UNUSED_DATA __attribute__ ((unused)) gconstpointer unused_data -+ -+/* avoid leak reports inside assertions; leaking stuff on assertion failures does not matter in tests */ -+#if !defined(__clang__) -+#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak" -+#pragma GCC diagnostic ignored "-Wanalyzer-file-leak" -+#endif -+ -+typedef struct { -+ pid_t thread; -+ libusb_context *ctx; -+ enum libusb_log_level level; -+ char *str; -+} LogMessage; -+ -+static void -+log_message_free(LogMessage *msg) -+{ -+ g_free(msg->str); -+ g_free(msg); -+} -+G_DEFINE_AUTOPTR_CLEANUP_FUNC(LogMessage, log_message_free) -+ -+typedef struct _UsbChat UsbChat; -+ -+struct _UsbChat { -+ gboolean submit; -+ gboolean reap; -+ UsbChat *reaps; -+ UsbChat *next; -+ -+ /* struct usbdevfs_urb */ -+ unsigned char type; -+ unsigned char endpoint; -+ int status; -+ unsigned int flags; -+ const unsigned char *buffer; -+ int buffer_length; -+ int actual_length; -+ -+ /* */ -+ UMockdevIoctlData *submit_urb; -+}; -+ -+typedef struct { -+ UMockdevTestbed *testbed; -+ UMockdevIoctlBase *handler; -+ struct libusb_context *ctx; -+ -+ gchar *root_dir; -+ gchar *sys_dir; -+ -+ gboolean libusb_log_silence; -+ GList *libusb_log; -+ -+ UsbChat *chat; -+ GList *flying_urbs; -+ GList *discarded_urbs; -+ -+ /* GMutex confuses tsan unecessarily */ -+ pthread_mutex_t mutex; -+} UMockdevTestbedFixture; -+ -+/* Global for log handler */ -+static UMockdevTestbedFixture *cur_fixture = NULL; -+ -+static void -+log_handler(libusb_context *ctx, enum libusb_log_level level, const char *str) -+{ -+ /* May be called from different threads without synchronization! */ -+ LogMessage *msg; -+ pid_t tid = gettid(); -+ -+ g_assert (cur_fixture != NULL); -+ g_assert(pthread_mutex_lock(&cur_fixture->mutex) == 0); -+ -+ msg = g_new0(LogMessage, 1); -+ msg->ctx = ctx; -+ msg->level = level; -+ msg->str = g_strchomp (g_strdup(str)); -+ msg->thread = tid; -+ -+ if (!cur_fixture->libusb_log_silence) -+ g_printerr("%s\n", msg->str); -+ -+ cur_fixture->libusb_log = g_list_append(cur_fixture->libusb_log, msg); -+ pthread_mutex_unlock(&cur_fixture->mutex); -+} -+ -+static void -+log_handler_null(libusb_context *ctx, enum libusb_log_level level, const char *str) -+{ -+ (void) ctx; -+ (void) level; -+ (void) str; -+} -+ -+static void -+clear_libusb_log(UMockdevTestbedFixture * fixture, enum libusb_log_level level) -+{ -+ g_assert(pthread_mutex_lock(&fixture->mutex) == 0); -+ -+ while (fixture->libusb_log) { -+ LogMessage *msg = fixture->libusb_log->data; -+ -+ g_assert(msg->ctx == fixture->ctx); -+ -+ if (msg->level < level) { -+ pthread_mutex_unlock(&fixture->mutex); -+ return; -+ } -+ -+ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log); -+ log_message_free(msg); -+ } -+ pthread_mutex_unlock(&fixture->mutex); -+} -+ -+static void -+assert_libusb_log_msg(UMockdevTestbedFixture * fixture, enum libusb_log_level level, const char *re) -+{ -+ g_assert(pthread_mutex_lock(&fixture->mutex) == 0); -+ -+ while (fixture->libusb_log) { -+ g_autoptr(LogMessage) msg = NULL; -+ -+ if (fixture->libusb_log == NULL) -+ g_error ("No level %d message found searching for %s", level, re); -+ -+ msg = fixture->libusb_log->data; -+ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log); -+ -+ if (msg->ctx != fixture->ctx) -+ g_error ("Saw unexpected message \"%s\" from context %p while %p was expected", -+ msg->str, msg->ctx, fixture->ctx); -+ -+ if (msg->level == level && g_regex_match_simple(re, msg->str, 0, 0)) { -+ pthread_mutex_unlock(&fixture->mutex); -+ return; -+ } -+ -+ /* Allow skipping INFO and DEBUG messages */ -+ if (msg->level >= LIBUSB_LOG_LEVEL_INFO) -+ continue; -+ -+ g_error ("Searched for \"%s\" (%d) but found \"%s\" (%d)", re, level, msg->str, msg->level); -+ } -+ -+ pthread_mutex_unlock(&fixture->mutex); -+ g_error ("Searched for \"%s\" (%d) but no message matched", re, level); -+} -+ -+#if 0 -+static void -+assert_libusb_no_log_msg(UMockdevTestbedFixture * fixture, enum libusb_log_level level, const char *re) -+{ -+ g_assert(pthread_mutex_lock(&fixture->mutex) == 0); -+ -+ while (fixture->libusb_log) { -+ g_autoptr(LogMessage) msg = NULL; -+ gboolean matching; -+ -+ msg = fixture->libusb_log->data; -+ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log); -+ -+ g_assert(msg->ctx == fixture->ctx); -+ -+ matching = (msg->level == level && g_regex_match_simple(re, msg->str, 0, 0)); -+ -+ /* Allow skipping INFO and DEBUG messages */ -+ if (!matching && msg->level >= LIBUSB_LOG_LEVEL_INFO) -+ continue; -+ -+ g_error ("Asserting \"%s\" (%d) not logged and found \"%s\" (%d)", re, level, msg->str, msg->level); -+ } -+ -+ pthread_mutex_unlock(&fixture->mutex); -+} -+#endif -+ -+static void -+dump_buffer(const unsigned char *buffer, int len) -+{ -+ g_autoptr(GString) line = NULL; -+ -+ line = g_string_new (""); -+ for (gint i = 0; i < len; i++) { -+ g_string_append_printf(line, "%02x ", buffer[i]); -+ if ((i + 1) % 16 == 0) { -+ g_printerr(" %s\n", line->str); -+ g_string_set_size(line, 0); -+ } -+ } -+ -+ if (line->len) -+ g_printerr(" %s\n", line->str); -+} -+ -+static gint -+cmp_ioctl_data_addr(const void *data, const void *addr) -+{ -+ return ((const UMockdevIoctlData*) data)->client_addr != (gulong) addr; -+} -+ -+static gboolean -+handle_ioctl_cb (UMockdevIoctlBase *handler, UMockdevIoctlClient *client, UMockdevTestbedFixture *fixture) -+{ -+ UMockdevIoctlData *ioctl_arg; -+ long int request; -+ struct usbdevfs_urb *urb; -+ -+ (void) handler; -+ -+ request = umockdev_ioctl_client_get_request (client); -+ ioctl_arg = umockdev_ioctl_client_get_arg (client); -+ -+ /* NOTE: We share the address space, dereferencing pointers *will* work. -+ * However, to make tsan work, we still stick to the API that resolves -+ * the data into a local copy! */ -+ -+ switch (request) { -+ case USBDEVFS_GET_CAPABILITIES: { -+ g_autoptr(UMockdevIoctlData) d = NULL; -+ d = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(guint32), NULL); -+ -+ *(guint32*) d->data = USBDEVFS_CAP_BULK_SCATTER_GATHER | -+ USBDEVFS_CAP_BULK_CONTINUATION | -+ USBDEVFS_CAP_NO_PACKET_SIZE_LIM | -+ USBDEVFS_CAP_REAP_AFTER_DISCONNECT | -+ USBDEVFS_CAP_ZERO_PACKET; -+ -+ umockdev_ioctl_client_complete(client, 0, 0); -+ return TRUE; -+ } -+ -+ case USBDEVFS_CLAIMINTERFACE: -+ case USBDEVFS_RELEASEINTERFACE: -+ case USBDEVFS_CLEAR_HALT: -+ case USBDEVFS_RESET: -+ case USBDEVFS_RESETEP: -+ umockdev_ioctl_client_complete(client, 0, 0); -+ return TRUE; -+ -+ case USBDEVFS_SUBMITURB: { -+ g_autoptr(UMockdevIoctlData) urb_buffer = NULL; -+ g_autoptr(UMockdevIoctlData) urb_data = NULL; -+ gsize buflen; -+ -+ if (!fixture->chat || !fixture->chat->submit) -+ return FALSE; -+ -+ buflen = fixture->chat->buffer_length; -+ if (fixture->chat->type == USBDEVFS_URB_TYPE_CONTROL) -+ buflen = 8; -+ -+ urb_data = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(struct usbdevfs_urb), NULL); -+ urb = (struct usbdevfs_urb*) urb_data->data; -+ urb_buffer = umockdev_ioctl_data_resolve(urb_data, G_STRUCT_OFFSET(struct usbdevfs_urb, buffer), urb->buffer_length, NULL); -+ -+ if (fixture->chat->type == urb->type && -+ fixture->chat->endpoint == urb->endpoint && -+ fixture->chat->buffer_length == urb->buffer_length && -+ (fixture->chat->buffer == NULL || memcmp (fixture->chat->buffer, urb_buffer->data, buflen) == 0)) { -+ fixture->flying_urbs = g_list_append (fixture->flying_urbs, umockdev_ioctl_data_ref(urb_data)); -+ -+ if (fixture->chat->reaps) -+ fixture->chat->reaps->submit_urb = urb_data; -+ -+ if (fixture->chat->status) -+ umockdev_ioctl_client_complete(client, -1, -fixture->chat->status); -+ else -+ umockdev_ioctl_client_complete(client, 0, 0); -+ -+ if (fixture->chat->next) -+ fixture->chat = fixture->chat->next; -+ else -+ fixture->chat += 1; -+ return TRUE; -+ } -+ -+ /* chat message didn't match, don't accept it */ -+ g_printerr("Could not process submit urb:\n"); -+ g_printerr(" t: %d, ep: %d, actual_length: %d, buffer_length: %d\n", -+ urb->type, urb->endpoint, urb->actual_length, urb->buffer_length); -+ if (urb->type == USBDEVFS_URB_TYPE_CONTROL || urb->endpoint & LIBUSB_ENDPOINT_IN) -+ dump_buffer(urb->buffer, urb->buffer_length); -+ g_printerr("Looking for:\n"); -+ g_printerr(" t: %d, ep: %d, actual_length: %d, buffer_length: %d\n", -+ fixture->chat->type, fixture->chat->endpoint, -+ fixture->chat->actual_length, fixture->chat->buffer_length); -+ if (fixture->chat->buffer) -+ dump_buffer(fixture->chat->buffer, buflen); -+ -+ return FALSE; -+ } -+ -+ case USBDEVFS_REAPURB: -+ case USBDEVFS_REAPURBNDELAY: { -+ g_autoptr(UMockdevIoctlData) urb_ptr = NULL; -+ g_autoptr(UMockdevIoctlData) urb_data = NULL; -+ -+ if (fixture->discarded_urbs) { -+ urb_data = fixture->discarded_urbs->data; -+ urb = (struct usbdevfs_urb*) urb_data->data; -+ fixture->discarded_urbs = g_list_delete_link(fixture->discarded_urbs, fixture->discarded_urbs); -+ urb->status = -ENOENT; -+ -+ urb_ptr = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(gpointer), NULL); -+ umockdev_ioctl_data_set_ptr(urb_ptr, 0, urb_data); -+ -+ umockdev_ioctl_client_complete(client, 0, 0); -+ return TRUE; -+ } -+ -+ if (fixture->chat && fixture->chat->reap) { -+ GList *l = g_list_find(fixture->flying_urbs, fixture->chat->submit_urb); -+ -+ if (l) { -+ fixture->flying_urbs = g_list_remove_link(fixture->flying_urbs, fixture->flying_urbs); -+ -+ urb_data = fixture->chat->submit_urb; -+ urb = (struct usbdevfs_urb*) urb_data->data; -+ urb->actual_length = fixture->chat->actual_length; -+ if (urb->type == USBDEVFS_URB_TYPE_CONTROL && urb->actual_length) -+ urb->actual_length -= 8; -+ if (fixture->chat->buffer) -+ memcpy(urb->buffer, fixture->chat->buffer, fixture->chat->actual_length); -+ urb->status = fixture->chat->status; -+ -+ urb_ptr = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(gpointer), NULL); -+ umockdev_ioctl_data_set_ptr(urb_ptr, 0, urb_data); -+ if (fixture->chat->next) -+ fixture->chat = fixture->chat->next; -+ else -+ fixture->chat += 1; -+ umockdev_ioctl_client_complete(client, 0, 0); -+ return TRUE; -+ } -+ } -+ -+ /* Nothing to reap */ -+ umockdev_ioctl_client_complete(client, -1, EAGAIN); -+ return TRUE; -+ } -+ -+ case USBDEVFS_DISCARDURB: { -+ GList *l = g_list_find_custom(fixture->flying_urbs, *(void**) ioctl_arg->data, cmp_ioctl_data_addr); -+ -+ if (l) { -+ fixture->discarded_urbs = g_list_append(fixture->discarded_urbs, l->data); -+ fixture->flying_urbs = g_list_delete_link(fixture->flying_urbs, l); -+ umockdev_ioctl_client_complete(client, 0, 0); -+ } else { -+ umockdev_ioctl_client_complete(client, -1, EINVAL); -+ } -+ -+ return TRUE; -+ } -+ -+ default: -+ return FALSE; -+ } -+} -+ -+static void -+test_fixture_add_canon(UMockdevTestbedFixture * fixture) -+{ -+ /* Setup first, so we can be sure libusb_open works when the add uevent -+ * happens. -+ */ -+ g_assert_cmpint(umockdev_testbed_attach_ioctl(fixture->testbed, "/dev/bus/usb/001/001", fixture->handler, NULL), ==, 1); -+ -+ /* NOTE: add_device would not create a file, needed for device emulation */ -+ /* XXX: Racy, see https://github.com/martinpitt/umockdev/issues/173 */ -+ umockdev_testbed_add_from_string(fixture->testbed, -+ "P: /devices/usb1\n" -+ "N: bus/usb/001/001\n" -+ "E: SUBSYSTEM=usb\n" -+ "E: DRIVER=usb\n" -+ "E: BUSNUM=001\n" -+ "E: DEVNUM=001\n" -+ "E: DEVNAME=/dev/bus/usb/001/001\n" -+ "E: DEVTYPE=usb_device\n" -+ "A: bConfigurationValue=1\\n\n" -+ "A: busnum=1\\n\n" -+ "A: devnum=1\\n\n" -+ "A: bConfigurationValue=1\\n\n" -+ "A: speed=480\\n\n" -+ /* descriptor from a Canon PowerShot SX200; VID 04a9 PID 31c0 */ -+ "H: descriptors=" -+ "1201000200000040a904c03102000102" -+ "030109022700010100c0010904000003" -+ "06010100070581020002000705020200" -+ "020007058303080009\n", -+ NULL); -+} -+ -+static void -+test_fixture_setup_libusb(UMockdevTestbedFixture * fixture, int devcount) -+{ -+ libusb_device **devs = NULL; -+ -+ libusb_init (&fixture->ctx); -+ -+ /* Supress global log messages completely -+ * (though, in some tests it might be interesting to check there are no real ones). -+ */ -+ libusb_set_log_cb (NULL, log_handler_null, LIBUSB_LOG_CB_GLOBAL); -+ libusb_set_option (fixture->ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG); -+ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, devcount); -+ libusb_free_device_list(devs, TRUE); -+ libusb_set_log_cb (fixture->ctx, log_handler, LIBUSB_LOG_CB_CONTEXT); -+} -+ -+static void -+test_fixture_setup_common(UMockdevTestbedFixture * fixture) -+{ -+ g_assert(cur_fixture == NULL); -+ cur_fixture = fixture; -+ -+ pthread_mutex_init(&fixture->mutex, NULL); -+ -+ fixture->testbed = umockdev_testbed_new(); -+ g_assert(fixture->testbed != NULL); -+ fixture->root_dir = umockdev_testbed_get_root_dir(fixture->testbed); -+ fixture->sys_dir = umockdev_testbed_get_sys_dir(fixture->testbed); -+ -+ fixture->handler = umockdev_ioctl_base_new(); -+ g_object_connect(fixture->handler, "signal-after::handle-ioctl", handle_ioctl_cb, fixture, NULL); -+} -+ -+#if 0 -+static void -+test_fixture_setup_empty(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ /* XXX: Racy, see https://github.com/martinpitt/umockdev/issues/173 */ -+ test_fixture_setup_common(fixture); -+ -+ test_fixture_setup_libusb(fixture, 0); -+} -+#endif -+ -+static void -+test_fixture_setup_with_canon(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ test_fixture_setup_common(fixture); -+ -+ test_fixture_add_canon(fixture); -+ -+ test_fixture_setup_libusb(fixture, 1); -+} -+ -+static void -+test_fixture_teardown(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ g_assert(cur_fixture == fixture); -+ -+ /* libusb_exit logs */ -+ if (fixture->ctx) -+ libusb_exit (fixture->ctx); -+ libusb_set_log_cb (NULL, NULL, LIBUSB_LOG_CB_GLOBAL); -+ cur_fixture = NULL; -+ -+ /* Abort if there are any warnings/errors in the log */ -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); -+ fixture->ctx = NULL; -+ g_assert_null(fixture->libusb_log); -+ -+ g_clear_object(&fixture->handler); -+ g_clear_object(&fixture->testbed); -+ -+ /* verify that temp dir gets cleaned up properly */ -+ g_assert(!g_file_test(fixture->root_dir, G_FILE_TEST_EXISTS)); -+ g_free(fixture->root_dir); -+ g_free(fixture->sys_dir); -+ -+ while (fixture->flying_urbs) { -+ umockdev_ioctl_data_unref (fixture->flying_urbs->data); -+ fixture->flying_urbs = g_list_delete_link (fixture->flying_urbs, fixture->flying_urbs); -+ } -+ -+ pthread_mutex_destroy(&fixture->mutex); -+} -+ -+static void -+test_open_close(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ libusb_device **devs = NULL; -+ struct libusb_device_descriptor desc; -+ libusb_device_handle *handle = NULL; -+ -+ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, 1); -+ /* The linux_enumerate_device may happen from a different thread */ -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_get_device_list"); -+ /* We have exactly one device */ -+ g_assert_cmpint(libusb_get_bus_number(devs[0]), ==, 1); -+ g_assert_cmpint(libusb_get_device_address(devs[0]), ==, 1); -+ -+ /* Get/Check descriptor */ -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); -+ libusb_get_device_descriptor (devs[0], &desc); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_get_device_descriptor"); -+ g_assert_cmpint(desc.idVendor, ==, 0x04a9); -+ g_assert_cmpint(desc.idProduct, ==, 0x31c0); -+ -+ /* Open and close */ -+ g_assert_cmpint(libusb_open(devs[0], &handle), ==, 0); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "usbi_add_event_source"); -+ g_assert_nonnull(handle); -+ libusb_close(handle); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "usbi_remove_event_source"); -+ -+ libusb_free_device_list(devs, TRUE); -+ -+ /* Open and close using vid/pid */ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ libusb_close(handle); -+} -+ -+static void -+test_close_flying(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ UsbChat chat[] = { -+ { -+ .submit = TRUE, -+ .type = USBDEVFS_URB_TYPE_BULK, -+ .endpoint = LIBUSB_ENDPOINT_OUT, -+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, -+ .buffer_length = 4, -+ }, -+ { .submit = FALSE } -+ }; -+ libusb_device_handle *handle = NULL; -+ struct libusb_transfer *transfer = NULL; -+ -+ fixture->chat = chat; -+ -+ /* Open */ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ -+ transfer = libusb_alloc_transfer(0); -+ libusb_fill_bulk_transfer(transfer, -+ handle, -+ LIBUSB_ENDPOINT_OUT, -+ (unsigned char*) chat[0].buffer, -+ chat[0].buffer_length, -+ NULL, -+ NULL, -+ 1); -+ -+ /* Submit */ -+ libusb_submit_transfer(transfer); -+ -+ /* Closing logs fat error (two lines) */ -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); -+ libusb_close(handle); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*connected as far as we know"); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*cancellation hasn't even been scheduled"); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[do_close\\] Removed transfer"); -+ -+ /* Free'ing the transfer works, and logs to the right context */ -+ libusb_free_transfer(transfer); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[libusb_free_transfer\\]"); -+} -+ -+static void -+test_close_cancelled(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ UsbChat chat[] = { -+ { -+ .submit = TRUE, -+ .type = USBDEVFS_URB_TYPE_BULK, -+ .endpoint = LIBUSB_ENDPOINT_OUT, -+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, -+ .buffer_length = 4, -+ }, -+ { .submit = FALSE } -+ }; -+ libusb_device_handle *handle = NULL; -+ struct libusb_transfer *transfer = NULL; -+ -+ fixture->chat = chat; -+ -+ /* Open */ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ -+ transfer = libusb_alloc_transfer(0); -+ libusb_fill_bulk_transfer(transfer, -+ handle, -+ LIBUSB_ENDPOINT_OUT, -+ (unsigned char*) chat[0].buffer, -+ chat[0].buffer_length, -+ NULL, -+ NULL, -+ 1); -+ -+ /* Submit */ -+ libusb_submit_transfer(transfer); -+ libusb_cancel_transfer(transfer); -+ -+ /* Closing logs fat error (two lines) */ -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); -+ libusb_close(handle); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*connected as far as we know"); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[do_close\\] .*cancellation.*hasn't completed"); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[do_close\\] Removed transfer"); -+ -+ libusb_free_transfer(transfer); -+} -+ -+static void -+test_ctx_destroy(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ UsbChat chat[] = { -+ { -+ .submit = TRUE, -+ .type = USBDEVFS_URB_TYPE_BULK, -+ .endpoint = LIBUSB_ENDPOINT_OUT, -+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, -+ .buffer_length = 4, -+ }, -+ { .submit = FALSE } -+ }; -+ libusb_device_handle *handle = NULL; -+ struct libusb_transfer *transfer = NULL; -+ -+ fixture->chat = chat; -+ -+ /* Open */ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ -+ transfer = libusb_alloc_transfer(0); -+ libusb_fill_bulk_transfer(transfer, -+ handle, -+ LIBUSB_ENDPOINT_OUT, -+ (unsigned char*) chat[0].buffer, -+ chat[0].buffer_length, -+ NULL, -+ NULL, -+ 1); -+ -+ /* Submit */ -+ libusb_submit_transfer(transfer); -+ -+ /* Now we are evil and destroy the ctx! */ -+ libusb_exit(fixture->ctx); -+ -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[libusb_exit\\] device.*still referenced"); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[libusb_exit\\] application left some devices open"); -+ -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); -+ fixture->ctx = NULL; -+ -+ /* XXX: Closing crashes the application as it unref's the NULL pointer */ -+ /* libusb_close(handle); */ -+ -+ libusb_free_transfer(transfer); -+} -+ -+static void -+test_get_string_descriptor(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ unsigned char data[255] = { 0, }; -+ libusb_device_handle *handle = NULL; -+ UsbChat chat[] = { -+ { -+ .submit = TRUE, -+ .reaps = &chat[1], -+ .type = USBDEVFS_URB_TYPE_CONTROL, -+ .buffer_length = 12, /* 8 byte out*/ -+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00", -+ }, { -+ /* String with content 0x0409 (en_US) */ -+ .reap = TRUE, -+ .actual_length = 12, -+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00\x04\x03\x09\x04", -+ }, { -+ .submit = TRUE, -+ .reaps = &chat[3], -+ .type = USBDEVFS_URB_TYPE_CONTROL, -+ .buffer_length = 263, /* 8 byte out*/ -+ .buffer = (const unsigned char*) "\x80\x06\x01\x03\x09\x04\xff\x00", -+ }, { -+ /* 4 byte string, "ab" */ -+ .reap = TRUE, -+ .actual_length = 14, -+ .buffer = (const unsigned char*) "\x80\x06\x01\x03\x09\x04\xff\x00\x06\x03\x61\x00\x62\x00", -+ }, { -+ .submit = TRUE, -+ .reaps = &chat[5], -+ .type = USBDEVFS_URB_TYPE_CONTROL, -+ .buffer_length = 12, /* 8 byte out*/ -+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00", -+ }, { -+ .reap = TRUE, -+ .status = -ENOENT, -+ }, { -+ .submit = TRUE, -+ .status = -ENOENT, -+ .type = USBDEVFS_URB_TYPE_CONTROL, -+ .buffer_length = 12, /* 8 byte out*/ -+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00", -+ }, { -+ .submit = FALSE, -+ } -+ }; -+ -+ fixture->chat = chat; -+ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ -+ /* The chat allows us to fetch the descriptor */ -+ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, 2); -+ g_assert_cmpint(memcmp(data, "ab", 2), ==, 0); -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); -+ -+ /* Again, but the URB fails with ENOENT when reaping */ -+ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, -1); -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); -+ -+ /* Again, but the URB fails to submit with ENOENT */ -+ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, -1); -+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[submit_control_transfer\\] submiturb failed, errno=2"); -+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG); -+ -+ libusb_close(handle); -+} -+ -+static void -+transfer_cb_inc_user_data(struct libusb_transfer *transfer) -+{ -+ *(int*)transfer->user_data += 1; -+} -+ -+static void -+test_timeout(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ UsbChat chat[] = { -+ { -+ .submit = TRUE, -+ .type = USBDEVFS_URB_TYPE_BULK, -+ .endpoint = LIBUSB_ENDPOINT_OUT, -+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 }, -+ .buffer_length = 4, -+ }, -+ { -+ .submit = FALSE, -+ } -+ }; -+ int completed = 0; -+ libusb_device_handle *handle = NULL; -+ struct libusb_transfer *transfer = NULL; -+ -+ fixture->chat = chat; -+ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ -+ transfer = libusb_alloc_transfer(0); -+ libusb_fill_bulk_transfer(transfer, -+ handle, -+ LIBUSB_ENDPOINT_OUT, -+ (unsigned char*) chat[0].buffer, -+ chat[0].buffer_length, -+ transfer_cb_inc_user_data, -+ &completed, -+ 10); -+ -+ libusb_submit_transfer(transfer); -+ while (!completed) { -+ g_assert_cmpint(libusb_handle_events_completed(fixture->ctx, &completed), ==, 0); -+ /* Silence after one iteration. */ -+ fixture->libusb_log_silence = TRUE; -+ } -+ fixture->libusb_log_silence = FALSE; -+ -+ g_assert_cmpint(transfer->status, ==, LIBUSB_TRANSFER_TIMED_OUT); -+ libusb_free_transfer(transfer); -+ -+ libusb_close(handle); -+} -+ -+#define THREADED_SUBMIT_URB_SETS 64 -+#define THREADED_SUBMIT_URB_IN_FLIGHT 64 -+typedef struct { -+ struct libusb_transfer *transfers[THREADED_SUBMIT_URB_IN_FLIGHT * THREADED_SUBMIT_URB_SETS]; -+ int submitted; -+ int completed; -+ int done; -+ UMockdevTestbedFixture *fixture; -+} TestThreadedSubmit; -+ -+static gpointer -+transfer_submit_all_retry(TestThreadedSubmit *data) -+{ -+ for (guint i = 0; i < G_N_ELEMENTS(data->transfers); i++) { -+ while (libusb_submit_transfer(data->transfers[i]) < 0) { -+ assert_libusb_log_msg(data->fixture, LIBUSB_LOG_LEVEL_ERROR, "submit_bulk_transfer"); -+ continue; -+ } -+ -+ data->submitted += 1; -+ } -+ -+ return NULL; -+} -+ -+static void -+test_threaded_submit_transfer_cb(struct libusb_transfer *transfer) -+{ -+ TestThreadedSubmit *data = transfer->user_data; -+ -+ /* We should only be receiving packets in the main thread */ -+ g_assert_cmpint (getpid(), ==, gettid()); -+ -+ /* Check that the transfer buffer has the expected value */ -+ g_assert_cmpint (*(int*)transfer->buffer, ==, data->completed); -+ data->completed += 1; -+ -+ if (data->completed == G_N_ELEMENTS(data->transfers)) -+ data->done = TRUE; -+} -+ -+static void -+test_threaded_submit(UMockdevTestbedFixture * fixture, UNUSED_DATA) -+{ -+ GThread *thread = NULL; -+ TestThreadedSubmit data = { .fixture = fixture }; -+ UsbChat out_msg = { -+ .submit = TRUE, -+ .type = USBDEVFS_URB_TYPE_BULK, -+ .endpoint = LIBUSB_ENDPOINT_IN, -+ .buffer_length = sizeof(int), -+ }; -+ UsbChat in_msg = { -+ .reap = TRUE, -+ .actual_length = 4, -+ }; -+ UsbChat *c; -+ libusb_device_handle *handle = NULL; -+ int urb; -+ -+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0); -+ g_assert_nonnull(handle); -+ -+ fixture->libusb_log_silence = TRUE; -+ -+ c = fixture->chat = g_new0(UsbChat, G_N_ELEMENTS(data.transfers) * 2 + 1); -+ urb = 0; -+ for (int i = 0; i < THREADED_SUBMIT_URB_SETS; i++) { -+ for (int j = 0; j < THREADED_SUBMIT_URB_IN_FLIGHT; j++) { -+ c[i*2*THREADED_SUBMIT_URB_IN_FLIGHT + j] = out_msg; -+ c[i*2*THREADED_SUBMIT_URB_IN_FLIGHT + j].reaps = &c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j]; -+ c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j] = in_msg; -+ c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j].buffer = (unsigned char*) g_new0(int, 1); -+ *(int*) c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j].buffer = urb; -+ -+ data.transfers[urb] = libusb_alloc_transfer(0); -+ libusb_fill_bulk_transfer(data.transfers[urb], -+ handle, -+ LIBUSB_ENDPOINT_IN, -+ g_malloc(out_msg.buffer_length), -+ out_msg.buffer_length, -+ test_threaded_submit_transfer_cb, -+ &data, -+ G_MAXUINT); -+ data.transfers[urb]->flags = LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER; -+ urb++; -+ } -+ } -+ -+ thread = g_thread_new("transfer all", (GThreadFunc) transfer_submit_all_retry, &data); -+ -+ while (!data.done) -+ g_assert_cmpint(libusb_handle_events_completed(fixture->ctx, &data.done), ==, 0); -+ -+ g_thread_join(thread); -+ -+ fixture->libusb_log_silence = FALSE; -+ libusb_close(handle); -+ -+ for (int i = 0; i < 2 * THREADED_SUBMIT_URB_SETS * THREADED_SUBMIT_URB_SETS; i++) -+ g_clear_pointer ((void**) &c->buffer, g_free); -+ g_free (c); -+} -+ -+int -+main(int argc, char **argv) -+{ -+ g_test_init(&argc, &argv, NULL); -+ -+ g_test_add("/libusb/open-close", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_open_close, -+ test_fixture_teardown); -+ -+ g_test_add("/libusb/close-flying", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_close_flying, -+ test_fixture_teardown); -+ g_test_add("/libusb/close-cancelled", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_close_cancelled, -+ test_fixture_teardown); -+ -+ g_test_add("/libusb/ctx-destroy", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_ctx_destroy, -+ test_fixture_teardown); -+ -+ g_test_add("/libusb/string-descriptor", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_get_string_descriptor, -+ test_fixture_teardown); -+ -+ g_test_add("/libusb/timeout", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_timeout, -+ test_fixture_teardown); -+ -+ g_test_add("/libusb/threaded-submit", UMockdevTestbedFixture, NULL, -+ test_fixture_setup_with_canon, -+ test_threaded_submit, -+ test_fixture_teardown); -+ -+ return g_test_run(); -+} --- -2.35.1 - diff --git a/1058.patch b/1058.patch deleted file mode 100644 index d504455..0000000 --- a/1058.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 2529a3fc4f987f93e0774af865ac7cb6557bd0c2 Mon Sep 17 00:00:00 2001 -From: Benjamin Berg -Date: Fri, 4 Feb 2022 22:50:28 +0100 -Subject: [PATCH] core: Unset device ctx if it has been destroyed - -Devices can outlive their context in some cases (in particular with -python garbage collection). Guard against this happening by clearing the -ctx pointer so that it is not pointing to uninitialized memory. ---- - libusb/core.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/libusb/core.c b/libusb/core.c -index 7893ac23..1c1ada14 100644 ---- a/libusb/core.c -+++ b/libusb/core.c -@@ -2441,6 +2441,7 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) - for_each_device(_ctx, dev) { - usbi_warn(_ctx, "device %d.%d still referenced", - dev->bus_number, dev->device_address); -+ DEVICE_CTX(dev) = NULL; - } - - if (!list_empty(&_ctx->open_devs)) diff --git a/1073.patch b/1073.patch deleted file mode 100644 index 7ecce5b..0000000 --- a/1073.patch +++ /dev/null @@ -1,133 +0,0 @@ -From 1cb11c5ba0d1d1266fe4ddd70f29d081f9d16802 Mon Sep 17 00:00:00 2001 -From: Benjamin Berg -Date: Tue, 15 Feb 2022 11:13:41 +0100 -Subject: [PATCH] io: Track device in usbi_transfer - -transfer->dev_handle currently has the behaviour that it will be unset -if the device is closed. The sync API uses this fact to catch an error -case. - -In other cases, transfer->dev_handle will keep its value, which means -that if the transfer lives longer than the device handle, the pointer -becomes invalid. - -The transfer does however keep a reference to the device, which owns the -pointer to the context. As such, we can track this reference internal to -the transfer, and it is set while the transfer is in-flight. - -With this, switch the logging infrastructure to use itransfer->dev->ctx -while checking that itransfer->dev is non-NULL. - -Note that this was a regression caused by 6cae9c6dbd74 ("core: update -usbi_dbg to take the context as an argument"), specifically when -resolving the context while freeing a transfer after closing a device. - -Note that the transfer will now keep a reference to the device until it -is free'ed. This allows it to use the correct context for logging even -in libusb_free_transfer. - -The alternative to all this would be to just explicitly pass NULL to the -log handler in libusb_free_transfer. ---- - libusb/io.c | 20 ++++++++++++-------- - libusb/libusbi.h | 10 +++++++--- - 2 files changed, 19 insertions(+), 11 deletions(-) - -diff --git a/libusb/io.c b/libusb/io.c -index 0d2ac9ea..b919e9d9 100644 ---- a/libusb/io.c -+++ b/libusb/io.c -@@ -1344,6 +1344,8 @@ void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer) - - itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); - usbi_mutex_destroy(&itransfer->lock); -+ if (itransfer->dev) -+ libusb_unref_device(itransfer->dev); - - priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); - ptr = (unsigned char *)itransfer - priv_size; -@@ -1489,9 +1491,15 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) - { - struct usbi_transfer *itransfer = - LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); -- struct libusb_context *ctx = TRANSFER_CTX(transfer); -+ struct libusb_context *ctx; - int r; - -+ assert(transfer->dev_handle); -+ if (itransfer->dev) -+ libusb_unref_device(itransfer->dev); -+ itransfer->dev = libusb_ref_device(transfer->dev_handle->dev); -+ -+ ctx = HANDLE_CTX(transfer->dev_handle); - usbi_dbg(ctx, "transfer %p", transfer); - - /* -@@ -1551,8 +1559,6 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) - r = usbi_backend.submit_transfer(itransfer); - if (r == LIBUSB_SUCCESS) { - itransfer->state_flags |= USBI_TRANSFER_IN_FLIGHT; -- /* keep a reference to this device */ -- libusb_ref_device(transfer->dev_handle->dev); - } - usbi_mutex_unlock(&itransfer->lock); - -@@ -1659,7 +1665,6 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, - { - struct libusb_transfer *transfer = - USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); -- struct libusb_device_handle *dev_handle = transfer->dev_handle; - struct libusb_context *ctx = ITRANSFER_CTX(itransfer); - uint8_t flags; - int r; -@@ -1693,7 +1698,6 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, - * this point. */ - if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) - libusb_free_transfer(transfer); -- libusb_unref_device(dev_handle->dev); - return r; - } - -@@ -1727,10 +1731,10 @@ int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer) - * function will be called the next time an event handler runs. */ - void usbi_signal_transfer_completion(struct usbi_transfer *itransfer) - { -- libusb_device_handle *dev_handle = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->dev_handle; -+ struct libusb_device *dev = itransfer->dev; - -- if (dev_handle) { -- struct libusb_context *ctx = HANDLE_CTX(dev_handle); -+ if (dev) { -+ struct libusb_context *ctx = DEVICE_CTX(dev); - unsigned int event_flags; - - usbi_mutex_lock(&ctx->event_data_lock); -diff --git a/libusb/libusbi.h b/libusb/libusbi.h -index 158a9af5..5f0d5c2e 100644 ---- a/libusb/libusbi.h -+++ b/libusb/libusbi.h -@@ -329,10 +329,11 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, - #endif /* ENABLE_LOGGING */ - - #define DEVICE_CTX(dev) ((dev)->ctx) --#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) --#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) -+#define HANDLE_CTX(handle) ((handle) ? DEVICE_CTX((handle)->dev) : NULL) - #define ITRANSFER_CTX(itransfer) \ -- (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer))) -+ ((itransfer)->dev ? DEVICE_CTX((itransfer)->dev) : NULL) -+#define TRANSFER_CTX(transfer) \ -+ (ITRANSFER_CTX(LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer))) - - #define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN)) - #define IS_EPOUT(ep) (!IS_EPIN(ep)) -@@ -562,6 +563,9 @@ struct usbi_transfer { - uint32_t state_flags; /* Protected by usbi_transfer->lock */ - uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */ - -+ /* This is used for logging mostly. As long as it is set, the */ -+ struct libusb_device *dev; -+ - /* this lock is held during libusb_submit_transfer() and - * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate - * cancellation, submission-during-cancellation, etc). the OS backend diff --git a/changelog b/changelog index 5df34fe..c8df9bf 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,6 @@ +* Fri Sep 30 2022 Kate Hsuan 1.0.26-1 +- Update to 1.0.26 + * Wed Feb 02 2022 Benjamin Berg 1.0.25-1 - Update to 1.0.25 diff --git a/libusb1.spec b/libusb1.spec index e7c8582..7c112f7 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -1,6 +1,6 @@ Summary: Library for accessing USB devices Name: libusb1 -Version: 1.0.25 +Version: 1.0.26 Release: %autorelease Source0: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2 License: LGPLv2+ @@ -13,18 +13,6 @@ BuildRequires: gcc Provides: libusbx = %{version}-%{release} Obsoletes: libusbx < %{version}-%{release} -# Fix a crash after libusb_exit API has been misused -# https://bugzilla.redhat.com/show_bug.cgi?id=2050638 -Patch0001: https://github.com/libusb/libusb/pull/1058.patch -# Fix a crash if a transfer outlives closing the device -Patch0002: https://github.com/libusb/libusb/pull/1073.patch -# Add umockdev based tests from https://github.com/libusb/libusb/pull/1078 -Patch0003: 0001-tests-Add-some-umockdev-based-tests.patch - -# Work around API misuse in gutenprint -# https://bugzilla.redhat.com/show_bug.cgi?id=2056326 -Patch9999: 0001-core-Install-first-context-as-implicit-default.patch - %description This package provides a way for applications to access USB devices. diff --git a/sources b/sources index 75982ca..3b509af 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (libusb-1.0.25.tar.bz2) = f1e6e5577d4bd1ff136927dc66c615014a06ac332ddd797b1d1ad5f7b68e2405e66068dcb210e2f0ae3e31681603ef72efbd88bf7fbe0eb41ce700fdc3f92f9d +SHA512 (libusb-1.0.26.tar.bz2) = fcdb85c98f21639668693c2fd522814d440972d65883984c4ae53d0555bdbdb7e8c7a32199cd4b01113556a1eb5be7841b750cc73c9f6bda79bfe1af80914e71 From bf42a855b47f0569bc15a0d35859a26888206b1a Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 19 Jan 2023 17:43:11 +0000 Subject: [PATCH 21/42] Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild Signed-off-by: Fedora Release Engineering From 6952413693a520bfd86cdb0bc2f16a0347740923 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 20 Jul 2023 12:25:11 +0000 Subject: [PATCH 22/42] Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild Signed-off-by: Fedora Release Engineering From 758eaec57920708baedd333bf082b09c7db9e66f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Such=C3=BD?= Date: Tue, 19 Sep 2023 03:54:03 +0000 Subject: [PATCH 23/42] Migrate to SPDX license This is part of https://fedoraproject.org/wiki/Changes/SPDX_Licenses_Phase_2 --- libusb1.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libusb1.spec b/libusb1.spec index 7c112f7..5566b32 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -3,7 +3,7 @@ Name: libusb1 Version: 1.0.26 Release: %autorelease Source0: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2 -License: LGPLv2+ +License: LGPL-2.1-or-later URL: http://libusb.info BuildRequires: systemd-devel doxygen libtool BuildRequires: umockdev-devel From 31b51b7c839698911feec65a64a4b1ee5f749391 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sun, 21 Jan 2024 05:38:03 +0000 Subject: [PATCH 24/42] Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild From 1dcfc6520a691b620bcfe7e0b4ba5c5703e0f528 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 25 Jan 2024 03:51:42 +0000 Subject: [PATCH 25/42] Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild From 07032b3e9cef208a9aac794a416b04b7e0567e7c Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Mon, 4 Mar 2024 15:04:45 +0800 Subject: [PATCH 26/42] Update to 1.0.27 - Update to 1.0.27 - Add tests, including init_context, set_option, and stress_mt --- .gitignore | 1 + libusb1.spec | 17 +++++++++++++++-- sources | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 255da81..a91d702 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /libusb-1.0.24.tar.bz2 /libusb-1.0.25.tar.bz2 /libusb-1.0.26.tar.bz2 +/libusb-1.0.27.tar.bz2 diff --git a/libusb1.spec b/libusb1.spec index 5566b32..fb13dca 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -1,6 +1,6 @@ Summary: Library for accessing USB devices Name: libusb1 -Version: 1.0.26 +Version: 1.0.27 Release: %autorelease Source0: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2 License: LGPL-2.1-or-later @@ -9,6 +9,7 @@ BuildRequires: systemd-devel doxygen libtool BuildRequires: umockdev-devel BuildRequires: make BuildRequires: gcc +BuildRequires: patchelf # libusbx was removed in F34 Provides: libusbx = %{version}-%{release} Obsoletes: libusbx < %{version}-%{release} @@ -75,8 +76,15 @@ popd %install %{make_install} +patchelf --remove-rpath tests/init_context +patchelf --remove-rpath tests/set_option +patchelf --remove-rpath tests/stress +patchelf --remove-rpath tests/stress_mt mkdir -p $RPM_BUILD_ROOT%{_bindir} -install -m 755 tests/.libs/stress $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress +install -m 755 tests/init_context $RPM_BUILD_ROOT%{_bindir}/libusb-test-init_context +install -m 755 tests/set_option $RPM_BUILD_ROOT%{_bindir}/libusb-test-set_option +install -m 755 tests/stress $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress +install -m 755 tests/stress_mt $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress_mt install -m 755 tests/.libs/umockdev $RPM_BUILD_ROOT%{_bindir}/libusb-test-umockdev install -m 755 examples/.libs/testlibusb \ $RPM_BUILD_ROOT%{_bindir}/libusb-test-libusb @@ -91,6 +99,8 @@ rm $RPM_BUILD_ROOT%{_libdir}/*.la %check LD_LIBRARY_PATH=libusb/.libs ldd $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress +LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-init_context +LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-set_option LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-umockdev LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-libusb @@ -117,7 +127,10 @@ LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-example-listdevs %{_bindir}/libusb-example-fxload %{_bindir}/libusb-example-listdevs %{_bindir}/libusb-example-xusb +%{_bindir}/libusb-test-init_context +%{_bindir}/libusb-test-set_option %{_bindir}/libusb-test-stress +%{_bindir}/libusb-test-stress_mt %{_bindir}/libusb-test-umockdev %{_bindir}/libusb-test-libusb diff --git a/sources b/sources index 3b509af..959d93c 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (libusb-1.0.26.tar.bz2) = fcdb85c98f21639668693c2fd522814d440972d65883984c4ae53d0555bdbdb7e8c7a32199cd4b01113556a1eb5be7841b750cc73c9f6bda79bfe1af80914e71 +SHA512 (libusb-1.0.27.tar.bz2) = 42abbbcf2564f86cc2f05826aeefa6beb03c9f89dc9fe05bdfa351caa3dbd07713ce193daadecf29557604dd3cbc770c8031a78e1ad0a8df3627c3f551d28ff0 From 8ea613411939e224d9432f88f77c75980ce083e2 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Wed, 8 May 2024 11:09:02 +0800 Subject: [PATCH 27/42] Build the test code without a rpath --- libusb1.spec | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/libusb1.spec b/libusb1.spec index fb13dca..f5f609a 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -9,7 +9,6 @@ BuildRequires: systemd-devel doxygen libtool BuildRequires: umockdev-devel BuildRequires: make BuildRequires: gcc -BuildRequires: patchelf # libusbx was removed in F34 Provides: libusbx = %{version}-%{release} Obsoletes: libusbx < %{version}-%{release} @@ -61,6 +60,7 @@ This package contains tests and examples for %{name}. %autosetup -p1 -n libusb-%{version} chmod -x examples/*.c mkdir -p m4 +sed -i '/AM_LDFLAGS = -static/d' tests/Makefile.am %build @@ -76,15 +76,11 @@ popd %install %{make_install} -patchelf --remove-rpath tests/init_context -patchelf --remove-rpath tests/set_option -patchelf --remove-rpath tests/stress -patchelf --remove-rpath tests/stress_mt mkdir -p $RPM_BUILD_ROOT%{_bindir} -install -m 755 tests/init_context $RPM_BUILD_ROOT%{_bindir}/libusb-test-init_context -install -m 755 tests/set_option $RPM_BUILD_ROOT%{_bindir}/libusb-test-set_option -install -m 755 tests/stress $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress -install -m 755 tests/stress_mt $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress_mt +install -m 755 tests/.libs/init_context $RPM_BUILD_ROOT%{_bindir}/libusb-test-init_context +install -m 755 tests/.libs/set_option $RPM_BUILD_ROOT%{_bindir}/libusb-test-set_option +install -m 755 tests/.libs/stress $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress +install -m 755 tests/.libs/stress_mt $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress_mt install -m 755 tests/.libs/umockdev $RPM_BUILD_ROOT%{_bindir}/libusb-test-umockdev install -m 755 examples/.libs/testlibusb \ $RPM_BUILD_ROOT%{_bindir}/libusb-test-libusb From 60468ae8bb863271ed40c54372c27cab3f05780c Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 18 Jul 2024 15:22:46 +0000 Subject: [PATCH 28/42] Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild From 2e36c0bb649e4f10aa79b40e4fc1ee4934ad6975 Mon Sep 17 00:00:00 2001 From: Jean THOMAS Date: Thu, 17 Oct 2024 18:31:06 +0200 Subject: [PATCH 29/42] Add MinGW builds --- libusb1.spec | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/libusb1.spec b/libusb1.spec index f5f609a..ad65625 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -1,3 +1,5 @@ +%bcond mingw %[0%{?fedora} && !0%{?flatpak}] + Summary: Library for accessing USB devices Name: libusb1 Version: 1.0.27 @@ -13,6 +15,13 @@ BuildRequires: gcc Provides: libusbx = %{version}-%{release} Obsoletes: libusbx < %{version}-%{release} +%if %{with mingw} +BuildRequires: mingw32-filesystem >= 95 +BuildRequires: mingw32-gcc-c++ +BuildRequires: mingw64-filesystem >= 95 +BuildRequires: mingw64-gcc-c++ +%endif + %description This package provides a way for applications to access USB devices. @@ -55,6 +64,19 @@ Obsoletes: libusbx-tests-examples < %{version}-%{release} %description tests-examples This package contains tests and examples for %{name}. +%if %{with mingw} +%package -n mingw32-%{name} +Summary: MinGW Windows %{name} library + +%description -n mingw32-%{name} +MinGW Windows %{name} library. + +%package -n mingw64-%{name} +Summary: MinGW Windows %{name} library + +%description -n mingw64-%{name} +MinGW Windows %{name} library. +%endif %prep %autosetup -p1 -n libusb-%{version} @@ -64,6 +86,9 @@ sed -i '/AM_LDFLAGS = -static/d' tests/Makefile.am %build +mkdir %{_target_os} +pushd %{_target_os} +%define _configure ../configure %configure --disable-static --enable-examples-build %{make_build} pushd doc @@ -72,9 +97,17 @@ popd pushd tests make popd +popd + +%if %{with mingw} +# MinGW build +%mingw_configure --disable-static +%mingw_make_build +%endif %install +pushd %{_target_os} %{make_install} mkdir -p $RPM_BUILD_ROOT%{_bindir} install -m 755 tests/.libs/init_context $RPM_BUILD_ROOT%{_bindir}/libusb-test-init_context @@ -91,9 +124,15 @@ for i in fxload listdevs xusb; do $RPM_BUILD_ROOT%{_bindir}/libusb-example-$i done rm $RPM_BUILD_ROOT%{_libdir}/*.la +popd + +%if %{with mingw} +%mingw_make_install +%endif %check +pushd %{_target_os} LD_LIBRARY_PATH=libusb/.libs ldd $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-init_context LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-set_option @@ -101,6 +140,7 @@ LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-stress LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-umockdev LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-test-libusb LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-example-listdevs +popd %ldconfig_scriptlets @@ -117,7 +157,7 @@ LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-example-listdevs %{_libdir}/pkgconfig/libusb-1.0.pc %files devel-doc -%doc doc/api-1.0 examples/*.c +%doc %{_target_os}/doc/api-1.0 examples/*.c %files tests-examples %{_bindir}/libusb-example-fxload @@ -130,6 +170,23 @@ LD_LIBRARY_PATH=libusb/.libs $RPM_BUILD_ROOT%{_bindir}/libusb-example-listdevs %{_bindir}/libusb-test-umockdev %{_bindir}/libusb-test-libusb +%if %{with mingw} +%files -n mingw32-libusb1 +%license COPYING +%doc AUTHORS README ChangeLog +%{mingw32_bindir}/libusb-1.0.dll +%{mingw32_includedir}/libusb-1.0 +%{mingw32_libdir}/*.dll.a +%{mingw32_libdir}/pkgconfig/libusb-1.0.pc + +%files -n mingw64-libusb1 +%license COPYING +%doc AUTHORS README ChangeLog +%{mingw64_bindir}/libusb-1.0.dll +%{mingw64_includedir}/libusb-1.0 +%{mingw64_libdir}/*.dll.a +%{mingw64_libdir}/pkgconfig/libusb-1.0.pc +%endif %changelog %autochangelog From 31c1f013c137aec6e02c7865a447eea2674a6ec3 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Fri, 10 Jan 2025 11:34:56 +0800 Subject: [PATCH 30/42] Fix the build failure with automake-1.17 Reran autoconf, aclocal, and automake to ensure the configure works with automake-1.17. Resolves: #2335712 --- libusb1.spec | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libusb1.spec b/libusb1.spec index ad65625..d3fb530 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -83,6 +83,12 @@ MinGW Windows %{name} library. chmod -x examples/*.c mkdir -p m4 sed -i '/AM_LDFLAGS = -static/d' tests/Makefile.am +%if 0%{fedora} >= 42 +autoscan +aclocal +autoconf +automake --add-missing +%endif %build From abbe292d8bb8893e3046ccf041804fb63a5b80c1 Mon Sep 17 00:00:00 2001 From: Yaakov Selkowitz Date: Mon, 13 Jan 2025 11:00:27 -0500 Subject: [PATCH 31/42] Fix ELN build Possibly undefined macros must be ?-prefixed, and ELN (the future RHEL 11) is tracking rawhide and also has automake 1.17. --- libusb1.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libusb1.spec b/libusb1.spec index d3fb530..a87829a 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -83,7 +83,7 @@ MinGW Windows %{name} library. chmod -x examples/*.c mkdir -p m4 sed -i '/AM_LDFLAGS = -static/d' tests/Makefile.am -%if 0%{fedora} >= 42 +%if 0%{?fedora} >= 42 || 0%{?rhel} >= 11 autoscan aclocal autoconf From 8756bdd70118a82d24c90eddf6fd2a8792f893b3 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 17 Jan 2025 14:46:08 +0000 Subject: [PATCH 32/42] Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild From fbcadef2f4e518f48bf44391742fbb2b7a4963a1 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Mon, 20 Jan 2025 08:12:49 +0000 Subject: [PATCH 33/42] Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild From 6c14dc730329a5ea22febdd3a064c5626d8e2765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 30 Jan 2025 14:45:14 +0400 Subject: [PATCH 34/42] Provides/Obsoletes mingw-libusbx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau --- libusb1.spec | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libusb1.spec b/libusb1.spec index a87829a..d206ad7 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -20,6 +20,9 @@ BuildRequires: mingw32-filesystem >= 95 BuildRequires: mingw32-gcc-c++ BuildRequires: mingw64-filesystem >= 95 BuildRequires: mingw64-gcc-c++ +# mingw-libusbx was removed in F42 +Provides: mingw-libusbx = %{version}-%{release} +Obsoletes: mingw-libusbx < %{version}-%{release} %endif %description @@ -67,12 +70,16 @@ This package contains tests and examples for %{name}. %if %{with mingw} %package -n mingw32-%{name} Summary: MinGW Windows %{name} library +Provides: mingw32-libusbx = %{version}-%{release} +Obsoletes: mingw32-libusbx < %{version}-%{release} %description -n mingw32-%{name} MinGW Windows %{name} library. %package -n mingw64-%{name} Summary: MinGW Windows %{name} library +Provides: mingw64-libusbx = %{version}-%{release} +Obsoletes: mingw64-libusbx < %{version}-%{release} %description -n mingw64-%{name} MinGW Windows %{name} library. From 3af863595299934e476811046ddd09713ec06928 Mon Sep 17 00:00:00 2001 From: LuK1337 Date: Wed, 26 Mar 2025 14:08:58 +0100 Subject: [PATCH 35/42] Verify source code with GPG --- .gitignore | 1 + libusb1.keyring | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ libusb1.spec | 4 ++ sources | 1 + 4 files changed, 123 insertions(+) create mode 100644 libusb1.keyring diff --git a/.gitignore b/.gitignore index a91d702..41386a3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /libusb-1.0.25.tar.bz2 /libusb-1.0.26.tar.bz2 /libusb-1.0.27.tar.bz2 +/libusb-1.0.27.tar.bz2.asc diff --git a/libusb1.keyring b/libusb1.keyring new file mode 100644 index 0000000..b2ed3c3 --- /dev/null +++ b/libusb1.keyring @@ -0,0 +1,117 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Hostname: +Version: Hockeypuck 2.2 + +xsFNBF7yPL0BEADQc/2dx8H7a7r1SGYph5hmkszs0O9V/43m8XhNnbnFraXjmbEv +xm2wE6AuR301mjAqYSt/mphmH54z4GBbgmLBrK8TGdhlK0K11PeSudRN4jsLs+U3 +ErtkAHODmzyg7QiW3GWudP/lJQRSqNBoadeOdOsKMoJxm7T2a9fyyf8FR/FfShjv +NB62jSWq0x0WnglI/V/ZOi/mOnqoggCoWXLzwqbKasicvfNsTPJIsjiu24US6mif +nRllMWr/6aHyCOX6+x6PsQ35NF5C5B7b0c1fY7zU/UiM/JBF4HDf7jltzTIjHjho +jTwcEkCVmunW+jSwjsLcr/zkOsu1re0W/VJJNXOhSnNUDpM7t9FeSfJ0LGlXYnGI +5ZUCQ8w4RcKmkHYhepCjDVWYkCmxmTgO7LaAXZ5S0GeOoSDsvHNHYywAXNmB6A0s +3kv/8i3wT8K1w9972eYW+NA6T7BfdbNk/EKxZQ74eezpRWDDPEl/zehoHQoPO3m1 +N2b06nnSKLv263IJAPdpLPUJowYdWnvmw/wyakeBMRJdI1FsDkEdI2KAvQxRKHfU +/cTtMEJuGGR5qyze4jMHUuVqSvEsoXmSA2OLcWeZyn12jfd0CrGbCZ7jZ0R7Q1Ab +cZ7hPsLKtgKHKyrmAdlmTgpOb2Kk2LP4ar0tuDa02YcFFAAWdRY9pORI+wARAQAB +zSdUb3Jtb2QgVm9sZGVuIDx0b3Jtb2Qudm9sZGVuQGdtYWlsLmNvbT7CwZAEEwEI +ADsCGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AWIQTGgYc3myPenvxGZR4sgP9W +xoMKDgUCXvI9hAIZAQAKCRAsgP9WxoMKDpcrD/i7ejrtzMGhDbB+IS5vvoK/Vk+s +Oszn+Bi4kjq+S4wv93gByDQy5L8YHSecKS60Qi0XW3VP7qoMXaI10oo0+4pZjheM +Lz38Xh7nOhnmzKzyPgB9sg/KuuSvcy6dZZ120ye035uckO3qDIvrV6rG9sx9EV8d +rOKppgpXBhCC52bFp45S6bbWRLQrKlmWDNdMSQcknt86ntSqxNJDdbKoxL0JxSI8 +mB+XrM7TZvyP9eA0ZVy55cbm0ZwU2beJty72GB0Niz0ZiGWeoBcuotDkpAwou7/B +Worgonw5yLMjL4NatZXRhym7YTNvKVovLwuG7krScghDCuGo1VswHyRi8xkkuvJ2 +YS51UBpvLsrDeLlBNd8JzL/FuBgFohkXzXjezx3gEUJe0+mc4gPdHULh8q9suRvF +ewOuQshiqvRUacuKNYglqnxqM4aJxqO0BCNDofgnu8JYk+llXzKT5bKiIXHDMWwd +eq9Y4NJzruAAilqM0tc1iI+qDmD4SabEjAmGREPeirVrASfrZFrOKBwF0PQE9fVN +PsXdYCHhfXLjlEFVv5pmJkhw3euFoxDz3auZ6OhGo1ffCOZ62On5joiIRhhGQ57l +qpW3W2Ph9TmWLRtOwR7DgiP/qUCrngBmk+Vl3KdwmSECDTXnFFKtOIHHomHEziEV +wnjxNpVBwrvZZZkPwl0EExEIAB0WIQQsLnerYFFdSZykiO+jLYR2uvQdDAUCXvJG +mgAKCRCjLYR2uvQdDNyVAJ9qmD3ioM5cVU3t7h4YSb6FuZ7CvQCggtBzoovIo6UJ +WsMd6NvtKXSVsijNJ1Rvcm1vZCBWb2xkZW4gPGRlYmlhbi50b3Jtb2RAZ21haWwu +Y29tPsLBjgQTAQgAOBYhBMaBhzebI96e/EZlHiyA/1bGgwoOBQJe8jy9AhsDBQsJ +CAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJECyA/1bGgwoOFdQP/R3oBQ/fQTFoaRVK +Q7KOp0MI2Bo1l9kRYnjj+CxlFUIEKTs06AER55IUpt1bjh4drldLVwaFP8rx/V5A +62Z2yvIAhkrEKRFkTEbdnfH5S23VF9T8n2L4nZ6L0VBK8bgdZsiTKWk4aVy4YdQG +yUC8mcXq1beZS8WiL7X/aH81uO+Bwaszmwgi2/NHEGdTuE1jUIslWyOHGhEe5Ygo ++mEltm+PLdZJ9dJAEI3fWYl0Y+5y+eDNBBNXEsTiZ0R/7xFakcT2AWSnRPxbllJk +4tG8FTlnSU8WY3VODec0L04UFJE64Ywupae0Xqc7ycJNk72FG3VEDgQhZC6e/L+a +vSgCvzI9U8mdypxS9znyYblCGigR46M/CMzUp6oA78u3cHPUyL2fVYMm6FcQN1Bv +nIlDSgHZJjFdpmAqYPvs+LR4+8dLXgwUKdIufka8yLJ2W3x5HQMtqBkgL8QqJTt1 +PdDbAaZdA1RHPJU2rE7sBI5EOOnQJu1cdFMOAXQR7BUad2au5IJ2oxy+z1fsZZeh +1X8OjypTQuKrQA3oAgaAERu/qRC4c6SKG8bMMR+3tf6NVWlYS+gK2wGxXCM5DuEJ +LYlHj2vQ/xeauq9MR23rgufVrmmnPEawMnsM5dD2ArR9FIq+sOAeZM/SJQC/I5Zf +uM+khtkurI63QMKLxzJAJjeS/gmlwl0EExEIAB0WIQQsLnerYFFdSZykiO+jLYR2 +uvQdDAUCXvJGowAKCRCjLYR2uvQdDIIVAKDcEF7MFwV/xjr7M5bTkSITiLfn/gCe +OTXlJl/vmuXHcJl7GOPkxr5LrbvCwXMEEAEIAB0WIQQadivyo0Wke8RrYv5ySIMB +pYJEdwUCZAe/mAAKCRBySIMBpYJEd2hkEADCFMxOTauegxN33aW07lJAk6BiXX/f +Byyh3Qgk41tp7oU9Z78DsLrPAVTCYXgp24zNmYWAPIfbSdirtdg/C/TBvR1YVx0+ +7GzEnGo53/IvxVDEUzXbGNonCfZvsPybV25pvmkzZuOEf4fcTPAyS8AntVUfCkLf +ntEVP7+raF1sxnlEoYx9ApkXjC2cpbS4RuRIJVp3zKSrxCTvK3lejzhbFEMGKJiI +TP7cm1kkkLD3541VJbm8MYtkn0DaeCw4ctLMVpXh/5wWxVan8HBnNeYdfEdKSwYR +L+UvprxblHZZDI3FYh0BL6U8+XqWChzDg9KteRL/N9u73fqpmoCsal5Li5MyKQMW +BVzrhIgVAfofgcvNmHN1sfNQrlAJf2gj5WXp3t7UpQolb9i6TM7mbj+IEOk/qVU2 +kJUuBma8h9p8EgSu1VH8ES4iZZZJz7w7SisoOFpVBMYYwI76JYAm82kx23n6T8xs +5E1lOXFZMGLvCyd2JqZRdNdAIemCnG1+TBEgedbq5XVaxsApBgjlj7YxPliIwIQx +zc+VDedf+bzCxbjir4IbEQ2JSyJU2ktPhmyWRhVMZIePiA/3v+7Kx7tigBfjIEvs +kzxZFhiDq/Nr3eLagTIwz0ogy86+QUOzZW2n1YO0DsgmaVaoMg0Lg2cvj8CsAblm +G73QcwUWUZ5N4c7BTQRe8j3JARAAs11IfLfybhdX3yjbVzxPiJ3RzkFZBbHyYcL8 +NJYdpxOGEK5pLu7zOe7z+TQpW4mMfQunbHreABunjCPuZwvME4ekQva/pky7S9aj +dsm1HMVpoXNQ0cSD+WTkiJaDJC6LFH6+XDzrUK7Kp/6NGKCSwU5xXmZudSVdpCNu +ziE+KQ5qEXPT6P7H+1TLNKgZvxmksHA76+/ZahpVTCgVVMpTmlRa3jnH0MoNv5fw +UMuC7fx09zdqb09D1bBcjrTltVcO6Ij8yUnw5DaQS8y8boIsIIK9YaJHk7uIo1qz +ilT7a71GKmz1Cs90qmLvRpN8nJGY6q28BXyM68E1Wx7x720IgXTR/JL/j3dBYggi +l3GGdBLEwVPtAy8VeeiNGsJe1ZmYUYMc6rgOjghWZogjI5mJOqOXOs3IilicsRTy +SCP4x7uRquWWlNNyeVE17ScGiUqsNCyzzwQ3MKbASswNrKnu0iIBfdYyWF+wiyB+ +kr8o23QMA7TIJnRj++ShOSeoPNg0wOns97Yj4VobSvWBmiX+VjFWkhOQFY9QeFib +QX3iBcSUBZh4eilQMWOx4vD9usBF9NsvrZKvIXrQI456BsTzoKFspqlka9y4YISw +3fbGjfOSNXab2R5xEkHX8fF/u8Xs897kVIi/imRrVSgmzf3X4QdTLQJ2MdhH02lh +lYdkvecAEQEAAcLDqwQYAQgAIBYhBMaBhzebI96e/EZlHiyA/1bGgwoOBQJe8j3J +AhsCAj8JECyA/1bGgwoOwXMgBBkBCAAdFiEEnH6pSTnGnE+8Pb+oqgY5B577YbkF +Al7yPckACgkQqgY5B577YbkUig/3XOT/88S0edOfgNfFtntAYCj4w3NztXiRClFQ +FohRupjP7h6y24VgKD1I0595fCGs9YKl9MiI9PAxNUVdKD6WOcjrRL6B8eMhxle4 +MefL4UK5kvUKTn2QqE8GgwAqgFkn0wbdOOxPVmGtJ3tuS5Hok9nn9RHUkeMKvOeR +Hx38NyozjZxoUJ+3gFngliM1BKlR3Dq1XlvXz/7fWKzl3AkneLHfca/0yzB67qvs +3G6q0btyZqjp0GSrGSVUnqpK670b1l6DQd6raej76RPq8OsxP1DkfwVsyNQV/EN0 +atj+MsruUPBbesZ5oP/XFrQkjjDDIGhbmg0xB9Bxp8v+y9EiFB9LC4nmLvw9gn2c +K3j1JXdiKUVWzPMKdUrZ/Y5lksrn6a326zDOJZwT4/XYiclgM+vKQb1RWdXvbz3o +TpSyeCdKZQ845aNM1Q8AHJ2NVlGBbiMsFTmKnM/wcU8+6saWflF0JeiNgal0wcGv +mkossrOVQZh10959HT8Eb4Vzgf0MD4YATmM6CbGxv1tuDxhK12e8MDsI7wulM5OD +LWpb3zwgLU/O3IeinbRlr30lhvnTzgdYx5CgYqUYUm/MSb0+vWpr67smoBbRpWi4 +j2zcTtay/iNL9pFCLFegkJtXwLehh8sgEj28c/jOH2XEfOgEEniVM57dFONmn5ba +3xTKRSS8D/44K3JJSPi2urzO+wXtcbZ1QSWypTV8dI7zLImySMmBtU7GEKLey8kl +XAQBnzyKTFrsS60A0JiNGbzw75kAi2677jgvEtzz0QAxvJUCianFT9QCqcxQokh/ +W8klVaJGLucAD5CRTLc9F4TNGV1jsHf90McWWf/bKANz875PZUDqMDtQ6hqHUdn4 +AxVaLn1dAqn2ae3DQK043jViy7IivilQLLo5mmkGLs0bPQZgG4OBB0mgzS8Zt2/3 +zJUvS/ygea0vqMzleEMlBJXWMyh6S8upEJVGdJfuMfRbOpvRBXZULLKwBVLn/vcB +6QianT31AtxpWRtXjk52DxrqP85jMZtrlXWECmOanNM41cN/hoVVcXYLYYrtf8ZY +M4cjB744M3XqCjh8aw8p8sg/sMQ4yJMlLuS6tGR/4WS1EU+Rq3ukg5jFfAQ/PfXr +j4iCFjUBD4CnRAQIXhPCqMl6hFMZw61BpKFpZNLlJ205R+elqGBbrLibhu3uRAeF +xk23S035hxBZnC2CDQL7zLwnzk1DPx6ywS6ky2qENwISR9tNldehFuPHXnSf5/Dx +UzfWd2Tj35vxZDhKjJ1HiT3o++HKCRX9cP/cALsd5zvIxSVN6RRCUI2U8N+bk5/d +fKNq8Q4FX9TZFSBnWudih+bT74v5f4LwhidPgOiYugiLoJh2ZqIVvc7BTQRe8jy9 +ARAA3hNbQBs2edDhl5UllZix8FZ0WQ3jbT3areF6tC5hHHOsSqqDB1VemUhBgoko +Z0qawFAa6pjh5eS3PkRdIU+TpSJisMPpZ99Cgk6lk9hYHvRyVZJK/P6ahJBI3Iw5 +P4OFn/8JnvhiI4lMTjzjge7OaQaWD7NL1wooBhzP4ogIpbFFpN9f8u1cpkEXdBJL +LyrEIz0+Kmg7xtuVAEadjyvOd5C8Be6GwaBu2NH6jbejBYdsVkqC+mGxeCebHpus +mhjWjnHipekaukbpfM1inbN3dEnT7xHo4ii6SKX/HdMZIUWnhynY1qFvxfFXyd5n +s2Dw7T78/uwwnE+bTC5Tz4MSekEXr9sjWG8w28XbR2FbOkKE6YCslBFlsIMz4Si5 +mEBuHdPSf/lvHgQF7MIykNI73RWfRTLcoAdt/pCiEK3Q8SZFJmSsY8Wzd9LO4/hT +xxZrTuxQvTYJb+lOfaL3p8aCH4X5J9jUTnq83ZHO/s5KtsP5w1qL0TiDAMYSVf0w +v87eVM6iT1x9K9sXFevQAucsIBO7Vsj9/g6G73JDLixhH4VIw0FpCZtvbXc+mhPm +s12fldP+/TruvyJZCXCLjxrsggxM2xNIZI2IGbdSgROKmpBlWuBuTiATA+x//oEH +17YfQZJjtcEFxBv31QZD2TzyJ5DeOKd9zYyPxsioJxGrCc0AEQEAAcLBdgQYAQgA +IBYhBMaBhzebI96e/EZlHiyA/1bGgwoOBQJe8jy9AhsMAAoJECyA/1bGgwoO5hwP +/it3ZTpxrb+ZCY0ahPavfXmPaPR6PrVrcHCgvjZ1f7RKBuOy0iGkejg2DRvowC+w +DtjpFfY/QWcGHEdCGQFS7QRu19Q97DGfVY+fup1ccuH9TckFNYWAi9pFqoShRXHU +UBpKE57HvuuDAv7s1nIBNIs1pb0nc0Fjgtoogd6oRvIeciz00B2doNfHBeNPC+XP +msBsEbqva6hRxr3mtaQCL+JP0PZySLmi6rsuTpIYs5kDN6KUoHzVSyOvIeaInW3M +5Z2lVNjDVjRzfSoMf8SDKWZOxxT57hwVijtmol5Vczx80MydTz1SE7MCsyAo1kSK +N+dQT/Wqoso0rcFGhpCshJmRh0UAgs5+8TjJ+hDEnSOgDlEmYaDUGgGwLGHoGbbl +vCv19yR/aejjZsb0TprshvxK2SUwyTN1Iv7p6LzAxn8QkAZrslhe7zAdHp9nVuCn +NtLfN6M7WpF42uz7PLJP60wdNPYDHYdSkGiRs9KNPpmBY2PldkkA9IX1L5/TvcPR +mvzTnVVTlycj5N8bMrU4uIBxU3FwozpyNIfjQxRYXMe5GOU2eqtVYtxgeN7wqGZ8 +dFzovMtHTKPPhEJ8uFSd//npnP6QMZgQA53Heaif9EbyrXJBlzRTPfGvr61XyNQk +5p5PG8RVrp3CdwEGZrYEkh5U4WR+C7SPZccMApzKGcb3 +=sExt +-----END PGP PUBLIC KEY BLOCK----- diff --git a/libusb1.spec b/libusb1.spec index d206ad7..ccfffb1 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -5,12 +5,15 @@ Name: libusb1 Version: 1.0.27 Release: %autorelease Source0: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2 +Source1: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2.asc +Source2: https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xc68187379b23de9efc46651e2c80ff56c6830a0e#/%{name}.keyring License: LGPL-2.1-or-later URL: http://libusb.info BuildRequires: systemd-devel doxygen libtool BuildRequires: umockdev-devel BuildRequires: make BuildRequires: gcc +BuildRequires: gnupg2 # libusbx was removed in F34 Provides: libusbx = %{version}-%{release} Obsoletes: libusbx < %{version}-%{release} @@ -86,6 +89,7 @@ MinGW Windows %{name} library. %endif %prep +%{gpgverify} --keyring='%{SOURCE2}' --signature='%{SOURCE1}' --data='%{SOURCE0}' %autosetup -p1 -n libusb-%{version} chmod -x examples/*.c mkdir -p m4 diff --git a/sources b/sources index 959d93c..2f3574c 100644 --- a/sources +++ b/sources @@ -1 +1,2 @@ SHA512 (libusb-1.0.27.tar.bz2) = 42abbbcf2564f86cc2f05826aeefa6beb03c9f89dc9fe05bdfa351caa3dbd07713ce193daadecf29557604dd3cbc770c8031a78e1ad0a8df3627c3f551d28ff0 +SHA512 (libusb-1.0.27.tar.bz2.asc) = b7ba04e284a8c8296ab1ae69eec7a65f00771519d1d661b196caba65ef08666997fd34061217f415b6cdcc9dbd34935762c3c2fa8abc4328a60202d94000f682 From 200675a582de8930ee3723d38ac157c3850924c8 Mon Sep 17 00:00:00 2001 From: LuK1337 Date: Wed, 26 Mar 2025 14:12:07 +0100 Subject: [PATCH 36/42] Update to 1.0.28 --- .gitignore | 2 ++ libusb1.spec | 2 +- sources | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 41386a3..32cc235 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ /libusb-1.0.26.tar.bz2 /libusb-1.0.27.tar.bz2 /libusb-1.0.27.tar.bz2.asc +/libusb-1.0.28.tar.bz2 +/libusb-1.0.28.tar.bz2.asc diff --git a/libusb1.spec b/libusb1.spec index ccfffb1..f561179 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -2,7 +2,7 @@ Summary: Library for accessing USB devices Name: libusb1 -Version: 1.0.27 +Version: 1.0.28 Release: %autorelease Source0: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2 Source1: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2.asc diff --git a/sources b/sources index 2f3574c..ff2dc84 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (libusb-1.0.27.tar.bz2) = 42abbbcf2564f86cc2f05826aeefa6beb03c9f89dc9fe05bdfa351caa3dbd07713ce193daadecf29557604dd3cbc770c8031a78e1ad0a8df3627c3f551d28ff0 -SHA512 (libusb-1.0.27.tar.bz2.asc) = b7ba04e284a8c8296ab1ae69eec7a65f00771519d1d661b196caba65ef08666997fd34061217f415b6cdcc9dbd34935762c3c2fa8abc4328a60202d94000f682 +SHA512 (libusb-1.0.28.tar.bz2) = 0f4efa6b54e6195d2e5446652c2dc07358583e205d63bf438c4409511b8637d1700a71268c40499755747827d23cc730d9122267386f847bf781993c045c519f +SHA512 (libusb-1.0.28.tar.bz2.asc) = 3ba1a91d0cf70a8e419df684c0465d9ef149a568286a42ce8606ed2ac574e35a5eb05ae71bb4549652b489e77dd337b4e28f9d27e20b7dc5f5ead73eb3217e85 From 3d48eee863315966220e83cafe6e958fa119bb96 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Thu, 27 Mar 2025 15:16:45 +0800 Subject: [PATCH 37/42] Fix the automake 1.17 issue for the versions less than f43 and rhel11 Since automake 1.17 is in f43 and recent f42 and libusb1 1.0.28 is released with automake 1.17, automake has to be rerun in %prep for versions less than f43 to get the correct build scripts. --- libusb1.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libusb1.spec b/libusb1.spec index f561179..41f06f3 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -94,7 +94,7 @@ MinGW Windows %{name} library. chmod -x examples/*.c mkdir -p m4 sed -i '/AM_LDFLAGS = -static/d' tests/Makefile.am -%if 0%{?fedora} >= 42 || 0%{?rhel} >= 11 +%if (0%{?fedora} && 0%{?fedora} < 43) || (0%{?rhel} && 0%{?rhel} < 11) autoscan aclocal autoconf From 48b605aa370a50c50cd975a5f7c99553928afb31 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Thu, 24 Jul 2025 20:31:14 +0000 Subject: [PATCH 38/42] Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild From 1029314e654c68b560610bf1fc6a0764aa1a596c Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Tue, 5 Aug 2025 13:58:32 +0800 Subject: [PATCH 39/42] Update to the upstream version 1.0.29 and migrate to tmt test 1. Update to upstream version 1.0.29. 2. Migrate the test to tmt. Resolves: #2382956 #2385130 --- .fmf/version | 1 + .gitignore | 2 ++ libusb1.spec | 4 ++-- plans/integration.fmf | 11 +++++++++++ sources | 4 ++-- tests/integration/main.fmf | 6 ++++++ tests/{ => integration}/run.sh | 0 tests/tests.yml | 13 ------------- 8 files changed, 24 insertions(+), 17 deletions(-) create mode 100644 .fmf/version create mode 100644 plans/integration.fmf create mode 100644 tests/integration/main.fmf rename tests/{ => integration}/run.sh (100%) delete mode 100644 tests/tests.yml diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/.gitignore b/.gitignore index 32cc235..415c8df 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ /libusb-1.0.27.tar.bz2.asc /libusb-1.0.28.tar.bz2 /libusb-1.0.28.tar.bz2.asc +/libusb-1.0.29.tar.bz2 +/libusb-1.0.29.tar.bz2.asc diff --git a/libusb1.spec b/libusb1.spec index 41f06f3..6c2f458 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -2,7 +2,7 @@ Summary: Library for accessing USB devices Name: libusb1 -Version: 1.0.28 +Version: 1.0.29 Release: %autorelease Source0: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2 Source1: https://github.com/libusb/libusb/releases/download/v%{version}/libusb-%{version}.tar.bz2.asc @@ -94,7 +94,7 @@ MinGW Windows %{name} library. chmod -x examples/*.c mkdir -p m4 sed -i '/AM_LDFLAGS = -static/d' tests/Makefile.am -%if (0%{?fedora} && 0%{?fedora} < 43) || (0%{?rhel} && 0%{?rhel} < 11) +%if (0%{?fedora} && 0%{?fedora} < 44) || (0%{?rhel} && 0%{?rhel} < 11) autoscan aclocal autoconf diff --git a/plans/integration.fmf b/plans/integration.fmf new file mode 100644 index 0000000..2bb7504 --- /dev/null +++ b/plans/integration.fmf @@ -0,0 +1,11 @@ +summary: Basic smoke test +discover: + how: fmf +prepare: + - name: packages + how: install + package: + - glibc-common + - libusb1-tests-examples +execute: + how: tmt diff --git a/sources b/sources index ff2dc84..b3dcd4d 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (libusb-1.0.28.tar.bz2) = 0f4efa6b54e6195d2e5446652c2dc07358583e205d63bf438c4409511b8637d1700a71268c40499755747827d23cc730d9122267386f847bf781993c045c519f -SHA512 (libusb-1.0.28.tar.bz2.asc) = 3ba1a91d0cf70a8e419df684c0465d9ef149a568286a42ce8606ed2ac574e35a5eb05ae71bb4549652b489e77dd337b4e28f9d27e20b7dc5f5ead73eb3217e85 +SHA512 (libusb-1.0.29.tar.bz2) = 04f8bda8197c9ecf52709609b8fbfea762fd82ddb5cde153a7630b0e8ed557d42da8cbc44f2f593aa22fdd0762e16716300565d67adb0c5240d7f3723321f690 +SHA512 (libusb-1.0.29.tar.bz2.asc) = 57e12d3a58d8ac418e28f6d83a7cb8c0268ed5d2752707796aa5bf1f8bcf2a8083aa664d36ffcdcc96da62153afa792728662fa8230be9e2865d4a37003e199d diff --git a/tests/integration/main.fmf b/tests/integration/main.fmf new file mode 100644 index 0000000..486f50b --- /dev/null +++ b/tests/integration/main.fmf @@ -0,0 +1,6 @@ +summary: + Test for libusb1 +test: bash ./run.sh + + + diff --git a/tests/run.sh b/tests/integration/run.sh similarity index 100% rename from tests/run.sh rename to tests/integration/run.sh diff --git a/tests/tests.yml b/tests/tests.yml deleted file mode 100644 index 57b120c..0000000 --- a/tests/tests.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- -- hosts: localhost - roles: - # Running these tests does not require the sources - - role: standard-test-basic - tags: - - atomic - - classic - tests: - - test: - dir: . - run: run.sh - From 378cca96bd54eb3e369d50470b641b6b9c7419c4 Mon Sep 17 00:00:00 2001 From: Yaakov Selkowitz Date: Tue, 5 Aug 2025 10:18:38 -0400 Subject: [PATCH 40/42] Always regenerate autotools Since we're modifying a Makefile.am, the corresponding Makefile.in will have to be regenerated, and we should never rely on any release having matching autotools versions. --- libusb1.spec | 2 -- 1 file changed, 2 deletions(-) diff --git a/libusb1.spec b/libusb1.spec index 6c2f458..256a12d 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -94,12 +94,10 @@ MinGW Windows %{name} library. chmod -x examples/*.c mkdir -p m4 sed -i '/AM_LDFLAGS = -static/d' tests/Makefile.am -%if (0%{?fedora} && 0%{?fedora} < 44) || (0%{?rhel} && 0%{?rhel} < 11) autoscan aclocal autoconf automake --add-missing -%endif %build From 3c2e5bfcc55851d96c3beae4a1c3c0d01ea02a86 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 11 Aug 2025 18:21:04 +0200 Subject: [PATCH 41/42] Add patches to skip uninitialized devices Otherwise there is a race condition if libusb enumerates the devices while udev is processing the event. Resolves: #2346771 --- ...v-remove-UMOCKDEV_HOTPLUG-workaround.patch | 77 +++++++++++ ...return-initialized-devices-during-en.patch | 120 ++++++++++++++++++ libusb1.spec | 4 + 3 files changed, 201 insertions(+) create mode 100644 0001-tests-umockdev-remove-UMOCKDEV_HOTPLUG-workaround.patch create mode 100644 0002-linux_udev-only-return-initialized-devices-during-en.patch diff --git a/0001-tests-umockdev-remove-UMOCKDEV_HOTPLUG-workaround.patch b/0001-tests-umockdev-remove-UMOCKDEV_HOTPLUG-workaround.patch new file mode 100644 index 0000000..2bbe1bd --- /dev/null +++ b/0001-tests-umockdev-remove-UMOCKDEV_HOTPLUG-workaround.patch @@ -0,0 +1,77 @@ +From 76bbaf23ac3f1c91c93e82a6c0947c66fb246edc Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Thu, 20 Mar 2025 22:38:39 +0100 +Subject: [PATCH 1/2] tests/umockdev: remove UMOCKDEV_HOTPLUG workaround + +umockdev 0.17.7 is old enough to rely on it for testing. Remove the +workaround as it might not be compatible with the follow up changes. + +Signed-off-by: Benjamin Berg +--- + configure.ac | 8 ++------ + tests/umockdev.c | 7 ------- + 2 files changed, 2 insertions(+), 13 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 9b519234..abdbf0de 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -209,14 +209,10 @@ linux) + AC_CHECK_HEADER([libudev.h], [], [AC_MSG_ERROR([udev support requested but libudev header not installed])]) + AC_CHECK_LIB([udev], [udev_new], [], [AC_MSG_ERROR([udev support requested but libudev not installed])]) + +- # We can build umockdev tests (if available) ++ # We can build umockdev tests (if available); need 0.17.7 because hotplug was racy before + m4_ifdef([PKG_PROG_PKG_CONFIG],[ + PKG_PROG_PKG_CONFIG +- PKG_CHECK_MODULES([UMOCKDEV], [umockdev-1.0 >= 0.16.0], [ac_have_umockdev=yes], [ac_have_umockdev=no]) +- PKG_CHECK_MODULES([UMOCKDEV_HOTPLUG], [umockdev-1.0 >= 0.17.7], [ac_umockdev_hotplug=yes], [ac_umockdev_hotplug=no]) +- if test $ac_umockdev_hotplug = yes; then +- AC_DEFINE([UMOCKDEV_HOTPLUG], [1], [UMockdev hotplug code is not racy]) +- fi ++ PKG_CHECK_MODULES([UMOCKDEV], [umockdev-1.0 >= 0.17.7], [ac_have_umockdev=yes], [ac_have_umockdev=no]) + ], []) + else + AC_CHECK_HEADERS([asm/types.h]) +diff --git a/tests/umockdev.c b/tests/umockdev.c +index a2d457ee..f979ed8f 100644 +--- a/tests/umockdev.c ++++ b/tests/umockdev.c +@@ -971,7 +971,6 @@ hotplug_count_arrival_cb(libusb_context *ctx, + return 0; + } + +-#ifdef UMOCKDEV_HOTPLUG + static int LIBUSB_CALL + hotplug_count_removal_cb(libusb_context *ctx, + libusb_device *device, +@@ -987,7 +986,6 @@ hotplug_count_removal_cb(libusb_context *ctx, + + return 0; + } +-#endif + + static void + test_hotplug_enumerate(UMockdevTestbedFixture * fixture, UNUSED_DATA) +@@ -1041,7 +1039,6 @@ test_hotplug_enumerate(UMockdevTestbedFixture * fixture, UNUSED_DATA) + static void + test_hotplug_add_remove(UMockdevTestbedFixture * fixture, UNUSED_DATA) + { +-#ifdef UMOCKDEV_HOTPLUG + libusb_device **devs = NULL; + libusb_hotplug_callback_handle handle_add; + libusb_hotplug_callback_handle handle_remove; +@@ -1111,10 +1108,6 @@ test_hotplug_add_remove(UMockdevTestbedFixture * fixture, UNUSED_DATA) + + libusb_hotplug_deregister_callback(fixture->ctx, handle_add); + libusb_hotplug_deregister_callback(fixture->ctx, handle_remove); +-#else +- (void) fixture; +- g_test_skip("UMockdev is too old to test hotplug"); +-#endif + } + + int +-- +2.50.1 + diff --git a/0002-linux_udev-only-return-initialized-devices-during-en.patch b/0002-linux_udev-only-return-initialized-devices-during-en.patch new file mode 100644 index 0000000..fbc91c1 --- /dev/null +++ b/0002-linux_udev-only-return-initialized-devices-during-en.patch @@ -0,0 +1,120 @@ +From 04f1e292af11aa9b5faef4338d36716396beff1a Mon Sep 17 00:00:00 2001 +From: Benjamin Berg +Date: Thu, 20 Mar 2025 22:49:52 +0100 +Subject: [PATCH 2/2] linux_udev: only return initialized devices during + enumeration + +The udev enumerator can return devices for which udev has not finished +processing its rules. As udev has not finished handling its rules, the +application may not yet be permitted to open the device. + +For these devices a hotplug event will happen later on when udev sends +the "add" event. + +Change the enumerator to only return initialized devices when udev is +running. There is no nice API to check whether this is the case, so just +do the same access() call that udev_queue_get_udev_is_active() would +also do. + +Also add a umockdev test for this specific corner case. Note that other +tests need to be updated to work around poor umockdev emulation of the +udev behaviour. umockdev never writes udev database entries to its +/run/udev/data, which means that devices are only considered initialized +when an event has been received. + +Signed-off-by: Benjamin Berg +--- + libusb/os/linux_udev.c | 5 +++++ + tests/umockdev.c | 25 +++++++++++++++++++++++-- + 2 files changed, 28 insertions(+), 2 deletions(-) + +diff --git a/libusb/os/linux_udev.c b/libusb/os/linux_udev.c +index 9ec9eb17..fad4f2ca 100644 +--- a/libusb/os/linux_udev.c ++++ b/libusb/os/linux_udev.c +@@ -278,6 +278,11 @@ int linux_udev_scan_devices(struct libusb_context *ctx) + return LIBUSB_ERROR_OTHER; + } + ++ /* Only return initialized devices if udev is running. If it is not ++ * running, devices will never be marked as "initialized". ++ */ ++ if (access("/run/udev/control", F_OK) == 0) ++ udev_enumerate_add_match_is_initialized(enumerator); + udev_enumerate_add_match_subsystem(enumerator, "usb"); + udev_enumerate_add_match_property(enumerator, "DEVTYPE", "usb_device"); + udev_enumerate_scan_devices(enumerator); +diff --git a/tests/umockdev.c b/tests/umockdev.c +index f979ed8f..f09611f1 100644 +--- a/tests/umockdev.c ++++ b/tests/umockdev.c +@@ -409,6 +409,8 @@ test_fixture_add_canon(UMockdevTestbedFixture * fixture) + "E: DRIVER=usb\n" + "E: BUSNUM=001\n" + "E: DEVNUM=001\n" ++ "E: MAJOR=189\n" ++ "E: MINOR=128\n" + "E: DEVNAME=/dev/bus/usb/001/001\n" + "E: DEVTYPE=usb_device\n" + "A: bConfigurationValue=1\\n\n" +@@ -429,8 +431,7 @@ static void + test_fixture_setup_libusb(UMockdevTestbedFixture * fixture, int devcount) + { + libusb_device **devs = NULL; +- +- libusb_init_context(/*ctx=*/&fixture->ctx, /*options=*/NULL, /*num_options=*/0); ++ struct timeval zero_tv = { 0 }; + + /* Suppress global log messages completely + * (though, in some tests it might be interesting to check there are no real ones). +@@ -440,6 +441,11 @@ test_fixture_setup_libusb(UMockdevTestbedFixture * fixture, int devcount) + g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, devcount); + libusb_free_device_list(devs, TRUE); + libusb_set_log_cb (fixture->ctx, log_handler, LIBUSB_LOG_CB_CONTEXT); ++ ++ /* Get the udev "add" event out of the way, part of the workaround that ++ * umockdev does no create udev DB files. ++ */ ++ libusb_handle_events_timeout(fixture->ctx, &zero_tv); + } + + static void +@@ -464,6 +470,8 @@ test_fixture_setup_empty(UMockdevTestbedFixture * fixture, UNUSED_DATA) + { + test_fixture_setup_common(fixture); + ++ libusb_init_context(/*ctx=*/&fixture->ctx, /*options=*/NULL, /*num_options=*/0); ++ + test_fixture_setup_libusb(fixture, 0); + } + +@@ -472,6 +480,14 @@ test_fixture_setup_with_canon(UMockdevTestbedFixture * fixture, UNUSED_DATA) + { + test_fixture_setup_common(fixture); + ++ /* As of umockev 0.19.1 (and probably later) no entries in ++ * /run/udev/data are created. i.e. udev does not have a database, ++ * which means libudev will only consider devices as "initialized" if ++ * it received the uevent and does not need to read the database file. ++ * Creating the context first means the uevent will be received. ++ */ ++ libusb_init_context(/*ctx=*/&fixture->ctx, /*options=*/NULL, /*num_options=*/0); ++ + test_fixture_add_canon(fixture); + + test_fixture_setup_libusb(fixture, 1); +@@ -574,6 +590,11 @@ test_implicit_default(UMockdevTestbedFixture * fixture, UNUSED_DATA) + clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); + + libusb_init_context(/*ctx=*/NULL, /*options=*/NULL, /*num_options=*/0); ++ /* Work around the missing udev DB in umockdev by sending a "add" ++ * notification. It is fine if this umockdev quirk changes in the ++ * future. ++ */ ++ umockdev_testbed_uevent(fixture->testbed, "/sys/devices/usb1", "add"); + g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); + libusb_exit(NULL); + +-- +2.50.1 + diff --git a/libusb1.spec b/libusb1.spec index 256a12d..b934c1f 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -18,6 +18,10 @@ BuildRequires: gnupg2 Provides: libusbx = %{version}-%{release} Obsoletes: libusbx < %{version}-%{release} +Patch01: 0001-tests-umockdev-remove-UMOCKDEV_HOTPLUG-workaround.patch +Patch02: 0002-linux_udev-only-return-initialized-devices-during-en.patch + + %if %{with mingw} BuildRequires: mingw32-filesystem >= 95 BuildRequires: mingw32-gcc-c++ From 4a3bd6f9ecaa4e74bda93b1ad449fd51f00adf7b Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Fri, 29 Aug 2025 10:43:28 +0800 Subject: [PATCH 42/42] Revert "Add patches to skip uninitialized devices" (rhbz#2390535) The fix brought a regression to mess storage so revert the fix and then trying to find the better fixes. This reverts commit 3c2e5bfcc55851d96c3beae4a1c3c0d01ea02a86. --- ...v-remove-UMOCKDEV_HOTPLUG-workaround.patch | 77 ----------- ...return-initialized-devices-during-en.patch | 120 ------------------ libusb1.spec | 4 - 3 files changed, 201 deletions(-) delete mode 100644 0001-tests-umockdev-remove-UMOCKDEV_HOTPLUG-workaround.patch delete mode 100644 0002-linux_udev-only-return-initialized-devices-during-en.patch diff --git a/0001-tests-umockdev-remove-UMOCKDEV_HOTPLUG-workaround.patch b/0001-tests-umockdev-remove-UMOCKDEV_HOTPLUG-workaround.patch deleted file mode 100644 index 2bbe1bd..0000000 --- a/0001-tests-umockdev-remove-UMOCKDEV_HOTPLUG-workaround.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 76bbaf23ac3f1c91c93e82a6c0947c66fb246edc Mon Sep 17 00:00:00 2001 -From: Benjamin Berg -Date: Thu, 20 Mar 2025 22:38:39 +0100 -Subject: [PATCH 1/2] tests/umockdev: remove UMOCKDEV_HOTPLUG workaround - -umockdev 0.17.7 is old enough to rely on it for testing. Remove the -workaround as it might not be compatible with the follow up changes. - -Signed-off-by: Benjamin Berg ---- - configure.ac | 8 ++------ - tests/umockdev.c | 7 ------- - 2 files changed, 2 insertions(+), 13 deletions(-) - -diff --git a/configure.ac b/configure.ac -index 9b519234..abdbf0de 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -209,14 +209,10 @@ linux) - AC_CHECK_HEADER([libudev.h], [], [AC_MSG_ERROR([udev support requested but libudev header not installed])]) - AC_CHECK_LIB([udev], [udev_new], [], [AC_MSG_ERROR([udev support requested but libudev not installed])]) - -- # We can build umockdev tests (if available) -+ # We can build umockdev tests (if available); need 0.17.7 because hotplug was racy before - m4_ifdef([PKG_PROG_PKG_CONFIG],[ - PKG_PROG_PKG_CONFIG -- PKG_CHECK_MODULES([UMOCKDEV], [umockdev-1.0 >= 0.16.0], [ac_have_umockdev=yes], [ac_have_umockdev=no]) -- PKG_CHECK_MODULES([UMOCKDEV_HOTPLUG], [umockdev-1.0 >= 0.17.7], [ac_umockdev_hotplug=yes], [ac_umockdev_hotplug=no]) -- if test $ac_umockdev_hotplug = yes; then -- AC_DEFINE([UMOCKDEV_HOTPLUG], [1], [UMockdev hotplug code is not racy]) -- fi -+ PKG_CHECK_MODULES([UMOCKDEV], [umockdev-1.0 >= 0.17.7], [ac_have_umockdev=yes], [ac_have_umockdev=no]) - ], []) - else - AC_CHECK_HEADERS([asm/types.h]) -diff --git a/tests/umockdev.c b/tests/umockdev.c -index a2d457ee..f979ed8f 100644 ---- a/tests/umockdev.c -+++ b/tests/umockdev.c -@@ -971,7 +971,6 @@ hotplug_count_arrival_cb(libusb_context *ctx, - return 0; - } - --#ifdef UMOCKDEV_HOTPLUG - static int LIBUSB_CALL - hotplug_count_removal_cb(libusb_context *ctx, - libusb_device *device, -@@ -987,7 +986,6 @@ hotplug_count_removal_cb(libusb_context *ctx, - - return 0; - } --#endif - - static void - test_hotplug_enumerate(UMockdevTestbedFixture * fixture, UNUSED_DATA) -@@ -1041,7 +1039,6 @@ test_hotplug_enumerate(UMockdevTestbedFixture * fixture, UNUSED_DATA) - static void - test_hotplug_add_remove(UMockdevTestbedFixture * fixture, UNUSED_DATA) - { --#ifdef UMOCKDEV_HOTPLUG - libusb_device **devs = NULL; - libusb_hotplug_callback_handle handle_add; - libusb_hotplug_callback_handle handle_remove; -@@ -1111,10 +1108,6 @@ test_hotplug_add_remove(UMockdevTestbedFixture * fixture, UNUSED_DATA) - - libusb_hotplug_deregister_callback(fixture->ctx, handle_add); - libusb_hotplug_deregister_callback(fixture->ctx, handle_remove); --#else -- (void) fixture; -- g_test_skip("UMockdev is too old to test hotplug"); --#endif - } - - int --- -2.50.1 - diff --git a/0002-linux_udev-only-return-initialized-devices-during-en.patch b/0002-linux_udev-only-return-initialized-devices-during-en.patch deleted file mode 100644 index fbc91c1..0000000 --- a/0002-linux_udev-only-return-initialized-devices-during-en.patch +++ /dev/null @@ -1,120 +0,0 @@ -From 04f1e292af11aa9b5faef4338d36716396beff1a Mon Sep 17 00:00:00 2001 -From: Benjamin Berg -Date: Thu, 20 Mar 2025 22:49:52 +0100 -Subject: [PATCH 2/2] linux_udev: only return initialized devices during - enumeration - -The udev enumerator can return devices for which udev has not finished -processing its rules. As udev has not finished handling its rules, the -application may not yet be permitted to open the device. - -For these devices a hotplug event will happen later on when udev sends -the "add" event. - -Change the enumerator to only return initialized devices when udev is -running. There is no nice API to check whether this is the case, so just -do the same access() call that udev_queue_get_udev_is_active() would -also do. - -Also add a umockdev test for this specific corner case. Note that other -tests need to be updated to work around poor umockdev emulation of the -udev behaviour. umockdev never writes udev database entries to its -/run/udev/data, which means that devices are only considered initialized -when an event has been received. - -Signed-off-by: Benjamin Berg ---- - libusb/os/linux_udev.c | 5 +++++ - tests/umockdev.c | 25 +++++++++++++++++++++++-- - 2 files changed, 28 insertions(+), 2 deletions(-) - -diff --git a/libusb/os/linux_udev.c b/libusb/os/linux_udev.c -index 9ec9eb17..fad4f2ca 100644 ---- a/libusb/os/linux_udev.c -+++ b/libusb/os/linux_udev.c -@@ -278,6 +278,11 @@ int linux_udev_scan_devices(struct libusb_context *ctx) - return LIBUSB_ERROR_OTHER; - } - -+ /* Only return initialized devices if udev is running. If it is not -+ * running, devices will never be marked as "initialized". -+ */ -+ if (access("/run/udev/control", F_OK) == 0) -+ udev_enumerate_add_match_is_initialized(enumerator); - udev_enumerate_add_match_subsystem(enumerator, "usb"); - udev_enumerate_add_match_property(enumerator, "DEVTYPE", "usb_device"); - udev_enumerate_scan_devices(enumerator); -diff --git a/tests/umockdev.c b/tests/umockdev.c -index f979ed8f..f09611f1 100644 ---- a/tests/umockdev.c -+++ b/tests/umockdev.c -@@ -409,6 +409,8 @@ test_fixture_add_canon(UMockdevTestbedFixture * fixture) - "E: DRIVER=usb\n" - "E: BUSNUM=001\n" - "E: DEVNUM=001\n" -+ "E: MAJOR=189\n" -+ "E: MINOR=128\n" - "E: DEVNAME=/dev/bus/usb/001/001\n" - "E: DEVTYPE=usb_device\n" - "A: bConfigurationValue=1\\n\n" -@@ -429,8 +431,7 @@ static void - test_fixture_setup_libusb(UMockdevTestbedFixture * fixture, int devcount) - { - libusb_device **devs = NULL; -- -- libusb_init_context(/*ctx=*/&fixture->ctx, /*options=*/NULL, /*num_options=*/0); -+ struct timeval zero_tv = { 0 }; - - /* Suppress global log messages completely - * (though, in some tests it might be interesting to check there are no real ones). -@@ -440,6 +441,11 @@ test_fixture_setup_libusb(UMockdevTestbedFixture * fixture, int devcount) - g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, devcount); - libusb_free_device_list(devs, TRUE); - libusb_set_log_cb (fixture->ctx, log_handler, LIBUSB_LOG_CB_CONTEXT); -+ -+ /* Get the udev "add" event out of the way, part of the workaround that -+ * umockdev does no create udev DB files. -+ */ -+ libusb_handle_events_timeout(fixture->ctx, &zero_tv); - } - - static void -@@ -464,6 +470,8 @@ test_fixture_setup_empty(UMockdevTestbedFixture * fixture, UNUSED_DATA) - { - test_fixture_setup_common(fixture); - -+ libusb_init_context(/*ctx=*/&fixture->ctx, /*options=*/NULL, /*num_options=*/0); -+ - test_fixture_setup_libusb(fixture, 0); - } - -@@ -472,6 +480,14 @@ test_fixture_setup_with_canon(UMockdevTestbedFixture * fixture, UNUSED_DATA) - { - test_fixture_setup_common(fixture); - -+ /* As of umockev 0.19.1 (and probably later) no entries in -+ * /run/udev/data are created. i.e. udev does not have a database, -+ * which means libudev will only consider devices as "initialized" if -+ * it received the uevent and does not need to read the database file. -+ * Creating the context first means the uevent will be received. -+ */ -+ libusb_init_context(/*ctx=*/&fixture->ctx, /*options=*/NULL, /*num_options=*/0); -+ - test_fixture_add_canon(fixture); - - test_fixture_setup_libusb(fixture, 1); -@@ -574,6 +590,11 @@ test_implicit_default(UMockdevTestbedFixture * fixture, UNUSED_DATA) - clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO); - - libusb_init_context(/*ctx=*/NULL, /*options=*/NULL, /*num_options=*/0); -+ /* Work around the missing udev DB in umockdev by sending a "add" -+ * notification. It is fine if this umockdev quirk changes in the -+ * future. -+ */ -+ umockdev_testbed_uevent(fixture->testbed, "/sys/devices/usb1", "add"); - g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1); - libusb_exit(NULL); - --- -2.50.1 - diff --git a/libusb1.spec b/libusb1.spec index b934c1f..256a12d 100644 --- a/libusb1.spec +++ b/libusb1.spec @@ -18,10 +18,6 @@ BuildRequires: gnupg2 Provides: libusbx = %{version}-%{release} Obsoletes: libusbx < %{version}-%{release} -Patch01: 0001-tests-umockdev-remove-UMOCKDEV_HOTPLUG-workaround.patch -Patch02: 0002-linux_udev-only-return-initialized-devices-during-en.patch - - %if %{with mingw} BuildRequires: mingw32-filesystem >= 95 BuildRequires: mingw32-gcc-c++