nsresult
sbWinCreateAncestorDeviceFile(HANDLE*               aDevFile,
                              DEVINST               aDevInst,
                              const GUID*           aGUID,
                              DWORD                 aDesiredAccess,
                              DWORD                 aShareMode,
                              LPSECURITY_ATTRIBUTES aSecurityAttributes,
                              DWORD                 aCreationDisposition,
                              DWORD                 aFlagsAndAttributes,
                              HANDLE                aTemplateFile)
{
  // Validate arguments.
  NS_ENSURE_ARG_POINTER(aDevFile);

  // Function variables.
  CONFIGRET cfgRet;
  nsresult  rv;

  // Search for an ancestor device that has the specified interface.
  DEVINST ancestorDevInst;
  DEVINST devInst = aDevInst;
  while (1) {
    // Get the next ancestor.
    cfgRet = CM_Get_Parent(&ancestorDevInst, devInst, 0);
    if (cfgRet != CR_SUCCESS)
      return NS_ERROR_NOT_AVAILABLE;

    // Check if the ancestor has the interface.
    bool hasInterface;
    rv = sbWinDeviceHasInterface(ancestorDevInst, aGUID, &hasInterface);
    NS_ENSURE_SUCCESS(rv, rv);
    if (hasInterface)
      break;

    // Check the next ancestor.
    devInst = ancestorDevInst;
  }

  /* Create a device file for the ancestor. */
  return sbWinCreateDeviceFile(aDevFile,
                               ancestorDevInst,
                               aGUID,
                               aDesiredAccess,
                               aShareMode,
                               aSecurityAttributes,
                               aCreationDisposition,
                               aFlagsAndAttributes,
                               aTemplateFile);
}
nsresult
sbWinRegisterDeviceHandleNotification(HDEVNOTIFY* aDeviceNotification,
                                      HWND        aEventWindow,
                                      DEVINST     aDevInst,
                                      const GUID& aGUID)
{
  // Validate arguments.
  NS_ENSURE_ARG_POINTER(aDeviceNotification);

  // Function variables.
  nsresult rv;

  // Create a device file for notifications.
  sbAutoHANDLE deviceHandle;
  rv = sbWinCreateDeviceFile(deviceHandle.StartAssignment(),
                             aDevInst,
                             &aGUID,
                             0,
                             FILE_SHARE_READ | FILE_SHARE_WRITE,
                             NULL,
                             OPEN_EXISTING,
                             0,
                             NULL);
  NS_ENSURE_SUCCESS(rv, rv);

  // Register for device handle notifications.  The handle may be closed after
  // registration without affecting the registration.  Doing so avoids having
  // extra device file handles open.
  HDEVNOTIFY           deviceNotification;
  DEV_BROADCAST_HANDLE devBroadcast = {0};
  devBroadcast.dbch_size = sizeof(devBroadcast);
  devBroadcast.dbch_devicetype = DBT_DEVTYP_HANDLE;
  devBroadcast.dbch_handle = deviceHandle;
  deviceNotification = RegisterDeviceNotification(aEventWindow,
                                                  &devBroadcast,
                                                  0);
  NS_ENSURE_TRUE(deviceNotification, NS_ERROR_FAILURE);

  // Return results.
  *aDeviceNotification = deviceNotification;

  return NS_OK;
}
nsresult
sbWinGetSCSIProductInfo(DEVINST    aDevInst,
                        nsAString& aVendorID,
                        nsAString& aProductID)
{
  DWORD    byteCount;
  BOOL     success;
  errno_t  errno;
  nsresult rv;

  // Create a disk interface device file.
  sbAutoHANDLE diskHandle;
  GUID         guid = GUID_DEVINTERFACE_DISK;
  rv = sbWinCreateDeviceFile(diskHandle.StartAssignment(),
                             aDevInst,
                             &guid,
                             0,
                             FILE_SHARE_READ | FILE_SHARE_WRITE,
                             NULL,
                             OPEN_EXISTING,
                             0,
                             NULL);
  NS_ENSURE_SUCCESS(rv, rv);

  // Set up a storage property query to get the storage device descriptor.
  STORAGE_PROPERTY_QUERY storagePropertyQuery;
  memset(&storagePropertyQuery, 0, sizeof(storagePropertyQuery));
  storagePropertyQuery.PropertyId = StorageDeviceProperty;
  storagePropertyQuery.QueryType = PropertyStandardQuery;

  // Determine how many bytes are required to hold the full storage device
  // descriptor.
  STORAGE_DESCRIPTOR_HEADER storageDescriptorHeader;
  success = DeviceIoControl(diskHandle,
                            IOCTL_STORAGE_QUERY_PROPERTY,
                            &storagePropertyQuery,
                            sizeof(storagePropertyQuery),
                            &storageDescriptorHeader,
                            sizeof(storageDescriptorHeader),
                            &byteCount,
                            NULL);
  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
  NS_ENSURE_TRUE(byteCount == sizeof(storageDescriptorHeader),
                 NS_ERROR_FAILURE);

  // Allocate the storage device descriptor.
  sbAutoMemPtr<STORAGE_DEVICE_DESCRIPTOR>
    storageDeviceDescriptor = static_cast<PSTORAGE_DEVICE_DESCRIPTOR>
                                (malloc(storageDescriptorHeader.Size));
  NS_ENSURE_TRUE(storageDeviceDescriptor, NS_ERROR_OUT_OF_MEMORY);

  // Get the storage device descriptor.
  success = DeviceIoControl(diskHandle,
                            IOCTL_STORAGE_QUERY_PROPERTY,
                            &storagePropertyQuery,
                            sizeof(storagePropertyQuery),
                            storageDeviceDescriptor,
                            storageDescriptorHeader.Size,
                            &byteCount,
                            NULL);
  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
  NS_ENSURE_TRUE(byteCount == storageDescriptorHeader.Size, NS_ERROR_FAILURE);

  // Return results with trailing spaces trimmed.  SCSI inquiry vendor and
  // product IDs have trailing spaces as filler.
  aVendorID.AssignLiteral
              (reinterpret_cast<char*>(storageDeviceDescriptor.get()) +
               storageDeviceDescriptor->VendorIdOffset);
  aVendorID.Trim(" ", PR_FALSE, PR_TRUE);
  aProductID.AssignLiteral
               (reinterpret_cast<char*>(storageDeviceDescriptor.get()) +
                storageDeviceDescriptor->ProductIdOffset);
  aProductID.Trim(" ", PR_FALSE, PR_TRUE);

  return NS_OK;
}