|
|
f731ee |
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
f731ee |
From: Michael Chang <mchang@suse.com>
|
|
|
f731ee |
Date: Thu, 14 Jul 2016 17:48:45 +0800
|
|
|
f731ee |
Subject: [PATCH] efinet: Setting DNS server from UEFI protocol
|
|
|
f731ee |
|
|
|
f731ee |
In the URI device path node, any name rahter than address can be used for
|
|
|
f731ee |
looking up the resources so that DNS service become needed to get answer of the
|
|
|
f731ee |
name's address. Unfortunately the DNS is not defined in any of the device path
|
|
|
f731ee |
nodes so that we use the EFI_IP4_CONFIG2_PROTOCOL and EFI_IP6_CONFIG_PROTOCOL
|
|
|
f731ee |
to obtain it.
|
|
|
f731ee |
|
|
|
f731ee |
These two protcols are defined the sections of UEFI specification.
|
|
|
f731ee |
|
|
|
f731ee |
27.5 EFI IPv4 Configuration II Protocol
|
|
|
f731ee |
27.7 EFI IPv6 Configuration Protocol
|
|
|
f731ee |
|
|
|
f731ee |
include/grub/efi/api.h:
|
|
|
f731ee |
Add new structure and protocol UUID of EFI_IP4_CONFIG2_PROTOCOL and
|
|
|
f731ee |
EFI_IP6_CONFIG_PROTOCOL.
|
|
|
f731ee |
|
|
|
f731ee |
grub-core/net/drivers/efi/efinet.c:
|
|
|
f731ee |
Use the EFI_IP4_CONFIG2_PROTOCOL and EFI_IP6_CONFIG_PROTOCOL to obtain the list
|
|
|
f731ee |
of DNS server address for IPv4 and IPv6 respectively. The address of DNS
|
|
|
f731ee |
servers is structured into DHCPACK packet and feed into the same DHCP packet
|
|
|
f731ee |
processing functions to ensure the network interface is setting up the same way
|
|
|
f731ee |
it used to be.
|
|
|
f731ee |
|
|
|
f731ee |
Signed-off-by: Michael Chang <mchang@suse.com>
|
|
|
f731ee |
Signed-off-by: Ken Lin <ken.lin@hpe.com>
|
|
|
f731ee |
---
|
|
|
f731ee |
grub-core/net/drivers/efi/efinet.c | 163 +++++++++++++++++++++++++++++++++++++
|
|
|
f731ee |
include/grub/efi/api.h | 76 +++++++++++++++++
|
|
|
f731ee |
2 files changed, 239 insertions(+)
|
|
|
f731ee |
|
|
|
f731ee |
diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
|
|
|
f731ee |
index 6f75ff1f9d1..25616a9c6c8 100644
|
|
|
f731ee |
--- a/grub-core/net/drivers/efi/efinet.c
|
|
|
f731ee |
+++ b/grub-core/net/drivers/efi/efinet.c
|
|
|
f731ee |
@@ -34,6 +34,8 @@ GRUB_MOD_LICENSE ("GPLv3+");
|
|
|
f731ee |
/* GUID. */
|
|
|
f731ee |
static grub_efi_guid_t net_io_guid = GRUB_EFI_SIMPLE_NETWORK_GUID;
|
|
|
f731ee |
static grub_efi_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID;
|
|
|
f731ee |
+static grub_efi_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID;
|
|
|
f731ee |
+static grub_efi_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID;
|
|
|
f731ee |
|
|
|
f731ee |
static grub_err_t
|
|
|
f731ee |
send_card_buffer (struct grub_net_card *dev,
|
|
|
f731ee |
@@ -343,6 +345,125 @@ grub_efinet_findcards (void)
|
|
|
f731ee |
grub_free (handles);
|
|
|
f731ee |
}
|
|
|
f731ee |
|
|
|
f731ee |
+static grub_efi_handle_t
|
|
|
f731ee |
+grub_efi_locate_device_path (grub_efi_guid_t *protocol, grub_efi_device_path_t *device_path,
|
|
|
f731ee |
+ grub_efi_device_path_t **r_device_path)
|
|
|
f731ee |
+{
|
|
|
f731ee |
+ grub_efi_handle_t handle;
|
|
|
f731ee |
+ grub_efi_status_t status;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ status = efi_call_3 (grub_efi_system_table->boot_services->locate_device_path,
|
|
|
f731ee |
+ protocol, &device_path, &handle);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ if (status != GRUB_EFI_SUCCESS)
|
|
|
f731ee |
+ return 0;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ if (r_device_path)
|
|
|
f731ee |
+ *r_device_path = device_path;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ return handle;
|
|
|
f731ee |
+}
|
|
|
f731ee |
+
|
|
|
f731ee |
+static grub_efi_ipv4_address_t *
|
|
|
f731ee |
+grub_dns_server_ip4_address (grub_efi_device_path_t *dp, grub_efi_uintn_t *num_dns)
|
|
|
f731ee |
+{
|
|
|
f731ee |
+ grub_efi_handle_t hnd;
|
|
|
f731ee |
+ grub_efi_status_t status;
|
|
|
f731ee |
+ grub_efi_ip4_config2_protocol_t *conf;
|
|
|
f731ee |
+ grub_efi_ipv4_address_t *addrs;
|
|
|
f731ee |
+ grub_efi_uintn_t data_size = 1 * sizeof (grub_efi_ipv4_address_t);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ hnd = grub_efi_locate_device_path (&ip4_config_guid, dp, NULL);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ if (!hnd)
|
|
|
f731ee |
+ return 0;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ conf = grub_efi_open_protocol (hnd, &ip4_config_guid,
|
|
|
f731ee |
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ if (!conf)
|
|
|
f731ee |
+ return 0;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ addrs = grub_malloc (data_size);
|
|
|
f731ee |
+ if (!addrs)
|
|
|
f731ee |
+ return 0;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ status = efi_call_4 (conf->get_data, conf,
|
|
|
f731ee |
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER,
|
|
|
f731ee |
+ &data_size, addrs);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ if (status == GRUB_EFI_BUFFER_TOO_SMALL)
|
|
|
f731ee |
+ {
|
|
|
f731ee |
+ grub_free (addrs);
|
|
|
f731ee |
+ addrs = grub_malloc (data_size);
|
|
|
f731ee |
+ if (!addrs)
|
|
|
f731ee |
+ return 0;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ status = efi_call_4 (conf->get_data, conf,
|
|
|
f731ee |
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER,
|
|
|
f731ee |
+ &data_size, addrs);
|
|
|
f731ee |
+ }
|
|
|
f731ee |
+
|
|
|
f731ee |
+ if (status != GRUB_EFI_SUCCESS)
|
|
|
f731ee |
+ {
|
|
|
f731ee |
+ grub_free (addrs);
|
|
|
f731ee |
+ return 0;
|
|
|
f731ee |
+ }
|
|
|
f731ee |
+
|
|
|
f731ee |
+ *num_dns = data_size / sizeof (grub_efi_ipv4_address_t);
|
|
|
f731ee |
+ return addrs;
|
|
|
f731ee |
+}
|
|
|
f731ee |
+
|
|
|
f731ee |
+static grub_efi_ipv6_address_t *
|
|
|
f731ee |
+grub_dns_server_ip6_address (grub_efi_device_path_t *dp, grub_efi_uintn_t *num_dns)
|
|
|
f731ee |
+{
|
|
|
f731ee |
+ grub_efi_handle_t hnd;
|
|
|
f731ee |
+ grub_efi_status_t status;
|
|
|
f731ee |
+ grub_efi_ip6_config_protocol_t *conf;
|
|
|
f731ee |
+ grub_efi_ipv6_address_t *addrs;
|
|
|
f731ee |
+ grub_efi_uintn_t data_size = 1 * sizeof (grub_efi_ipv6_address_t);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ hnd = grub_efi_locate_device_path (&ip6_config_guid, dp, NULL);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ if (!hnd)
|
|
|
f731ee |
+ return 0;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ conf = grub_efi_open_protocol (hnd, &ip6_config_guid,
|
|
|
f731ee |
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ if (!conf)
|
|
|
f731ee |
+ return 0;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ addrs = grub_malloc (data_size);
|
|
|
f731ee |
+ if (!addrs)
|
|
|
f731ee |
+ return 0;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ status = efi_call_4 (conf->get_data, conf,
|
|
|
f731ee |
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER,
|
|
|
f731ee |
+ &data_size, addrs);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ if (status == GRUB_EFI_BUFFER_TOO_SMALL)
|
|
|
f731ee |
+ {
|
|
|
f731ee |
+ grub_free (addrs);
|
|
|
f731ee |
+ addrs = grub_malloc (data_size);
|
|
|
f731ee |
+ if (!addrs)
|
|
|
f731ee |
+ return 0;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ status = efi_call_4 (conf->get_data, conf,
|
|
|
f731ee |
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER,
|
|
|
f731ee |
+ &data_size, addrs);
|
|
|
f731ee |
+ }
|
|
|
f731ee |
+
|
|
|
f731ee |
+ if (status != GRUB_EFI_SUCCESS)
|
|
|
f731ee |
+ {
|
|
|
f731ee |
+ grub_free (addrs);
|
|
|
f731ee |
+ return 0;
|
|
|
f731ee |
+ }
|
|
|
f731ee |
+
|
|
|
f731ee |
+ *num_dns = data_size / sizeof (grub_efi_ipv6_address_t);
|
|
|
f731ee |
+ return addrs;
|
|
|
f731ee |
+}
|
|
|
f731ee |
+
|
|
|
f731ee |
static struct grub_net_buff *
|
|
|
f731ee |
grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6)
|
|
|
f731ee |
{
|
|
|
f731ee |
@@ -401,6 +522,8 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u
|
|
|
f731ee |
grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp;
|
|
|
f731ee |
struct grub_net_bootp_packet *bp;
|
|
|
f731ee |
grub_uint8_t *ptr;
|
|
|
f731ee |
+ grub_efi_ipv4_address_t *dns;
|
|
|
f731ee |
+ grub_efi_uintn_t num_dns;
|
|
|
f731ee |
|
|
|
f731ee |
bp = (struct grub_net_bootp_packet *) nb->tail;
|
|
|
f731ee |
err = grub_netbuff_put (nb, sizeof (*bp) + 4);
|
|
|
f731ee |
@@ -462,6 +585,25 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u
|
|
|
f731ee |
*ptr++ = sizeof ("HTTPClient") - 1;
|
|
|
f731ee |
grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1);
|
|
|
f731ee |
|
|
|
f731ee |
+ dns = grub_dns_server_ip4_address (dp, &num_dns);
|
|
|
f731ee |
+ if (dns)
|
|
|
f731ee |
+ {
|
|
|
f731ee |
+ grub_efi_uintn_t size_dns = sizeof (*dns) * num_dns;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ ptr = nb->tail;
|
|
|
f731ee |
+ err = grub_netbuff_put (nb, size_dns + 2);
|
|
|
f731ee |
+ if (err)
|
|
|
f731ee |
+ {
|
|
|
f731ee |
+ grub_free (ddp);
|
|
|
f731ee |
+ grub_netbuff_free (nb);
|
|
|
f731ee |
+ return NULL;
|
|
|
f731ee |
+ }
|
|
|
f731ee |
+ *ptr++ = GRUB_NET_BOOTP_DNS;
|
|
|
f731ee |
+ *ptr++ = size_dns;
|
|
|
f731ee |
+ grub_memcpy (ptr, dns, size_dns);
|
|
|
f731ee |
+ grub_free (dns);
|
|
|
f731ee |
+ }
|
|
|
f731ee |
+
|
|
|
f731ee |
ptr = nb->tail;
|
|
|
f731ee |
err = grub_netbuff_put (nb, 1);
|
|
|
f731ee |
if (err)
|
|
|
f731ee |
@@ -494,6 +636,8 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u
|
|
|
f731ee |
struct grub_net_dhcp6_option *opt;
|
|
|
f731ee |
struct grub_net_dhcp6_option_iana *iana;
|
|
|
f731ee |
struct grub_net_dhcp6_option_iaaddr *iaaddr;
|
|
|
f731ee |
+ grub_efi_ipv6_address_t *dns;
|
|
|
f731ee |
+ grub_efi_uintn_t num_dns;
|
|
|
f731ee |
|
|
|
f731ee |
d6p = (struct grub_net_dhcp6_packet *)nb->tail;
|
|
|
f731ee |
err = grub_netbuff_put (nb, sizeof(*d6p));
|
|
|
f731ee |
@@ -557,6 +701,25 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u
|
|
|
f731ee |
opt->len = grub_cpu_to_be16 (uri_len);
|
|
|
f731ee |
grub_memcpy (opt->data, uri_dp->uri, uri_len);
|
|
|
f731ee |
|
|
|
f731ee |
+ dns = grub_dns_server_ip6_address (dp, &num_dns);
|
|
|
f731ee |
+ if (dns)
|
|
|
f731ee |
+ {
|
|
|
f731ee |
+ grub_efi_uintn_t size_dns = sizeof (*dns) * num_dns;
|
|
|
f731ee |
+
|
|
|
f731ee |
+ opt = (struct grub_net_dhcp6_option *)nb->tail;
|
|
|
f731ee |
+ err = grub_netbuff_put (nb, sizeof(*opt) + size_dns);
|
|
|
f731ee |
+ if (err)
|
|
|
f731ee |
+ {
|
|
|
f731ee |
+ grub_free (ddp);
|
|
|
f731ee |
+ grub_netbuff_free (nb);
|
|
|
f731ee |
+ return NULL;
|
|
|
f731ee |
+ }
|
|
|
f731ee |
+ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS);
|
|
|
f731ee |
+ opt->len = grub_cpu_to_be16 (size_dns);
|
|
|
f731ee |
+ grub_memcpy (opt->data, dns, size_dns);
|
|
|
f731ee |
+ grub_free (dns);
|
|
|
f731ee |
+ }
|
|
|
f731ee |
+
|
|
|
f731ee |
*use_ipv6 = 1;
|
|
|
f731ee |
}
|
|
|
f731ee |
|
|
|
f731ee |
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
|
|
|
f731ee |
index 76ad56aa2b0..852daddfb0f 100644
|
|
|
f731ee |
--- a/include/grub/efi/api.h
|
|
|
f731ee |
+++ b/include/grub/efi/api.h
|
|
|
f731ee |
@@ -289,6 +289,16 @@
|
|
|
f731ee |
{ 0x8B, 0x8C, 0xE2, 0x1B, 0x01, 0xAE, 0xF2, 0xB7 } \
|
|
|
f731ee |
}
|
|
|
f731ee |
|
|
|
f731ee |
+#define GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID \
|
|
|
f731ee |
+ { 0x5b446ed1, 0xe30b, 0x4faa, \
|
|
|
f731ee |
+ { 0x87, 0x1a, 0x36, 0x54, 0xec, 0xa3, 0x60, 0x80 } \
|
|
|
f731ee |
+ }
|
|
|
f731ee |
+
|
|
|
f731ee |
+#define GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID \
|
|
|
f731ee |
+ { 0x937fe521, 0x95ae, 0x4d1a, \
|
|
|
f731ee |
+ { 0x89, 0x29, 0x48, 0xbc, 0xd9, 0x0a, 0xd3, 0x1a } \
|
|
|
f731ee |
+ }
|
|
|
f731ee |
+
|
|
|
f731ee |
struct grub_efi_sal_system_table
|
|
|
f731ee |
{
|
|
|
f731ee |
grub_uint32_t signature;
|
|
|
f731ee |
@@ -1785,6 +1795,72 @@ struct grub_efi_block_io
|
|
|
f731ee |
};
|
|
|
f731ee |
typedef struct grub_efi_block_io grub_efi_block_io_t;
|
|
|
f731ee |
|
|
|
f731ee |
+enum grub_efi_ip4_config2_data_type {
|
|
|
f731ee |
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO,
|
|
|
f731ee |
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_POLICY,
|
|
|
f731ee |
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
|
|
|
f731ee |
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY,
|
|
|
f731ee |
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER,
|
|
|
f731ee |
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MAXIMUM
|
|
|
f731ee |
+};
|
|
|
f731ee |
+typedef enum grub_efi_ip4_config2_data_type grub_efi_ip4_config2_data_type_t;
|
|
|
f731ee |
+
|
|
|
f731ee |
+struct grub_efi_ip4_config2_protocol
|
|
|
f731ee |
+{
|
|
|
f731ee |
+ grub_efi_status_t (*set_data) (struct grub_efi_ip4_config2_protocol *this,
|
|
|
f731ee |
+ grub_efi_ip4_config2_data_type_t data_type,
|
|
|
f731ee |
+ grub_efi_uintn_t data_size,
|
|
|
f731ee |
+ void *data);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ grub_efi_status_t (*get_data) (struct grub_efi_ip4_config2_protocol *this,
|
|
|
f731ee |
+ grub_efi_ip4_config2_data_type_t data_type,
|
|
|
f731ee |
+ grub_efi_uintn_t *data_size,
|
|
|
f731ee |
+ void *data);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ grub_efi_status_t (*register_data_notify) (struct grub_efi_ip4_config2_protocol *this,
|
|
|
f731ee |
+ grub_efi_ip4_config2_data_type_t data_type,
|
|
|
f731ee |
+ grub_efi_event_t event);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ grub_efi_status_t (*unregister_datanotify) (struct grub_efi_ip4_config2_protocol *this,
|
|
|
f731ee |
+ grub_efi_ip4_config2_data_type_t data_type,
|
|
|
f731ee |
+ grub_efi_event_t event);
|
|
|
f731ee |
+};
|
|
|
f731ee |
+typedef struct grub_efi_ip4_config2_protocol grub_efi_ip4_config2_protocol_t;
|
|
|
f731ee |
+
|
|
|
f731ee |
+enum grub_efi_ip6_config_data_type {
|
|
|
f731ee |
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO,
|
|
|
f731ee |
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_ALT_INTERFACEID,
|
|
|
f731ee |
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_POLICY,
|
|
|
f731ee |
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_DUP_ADDR_DETECT_TRANSMITS,
|
|
|
f731ee |
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
|
|
|
f731ee |
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_GATEWAY,
|
|
|
f731ee |
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER,
|
|
|
f731ee |
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_MAXIMUM
|
|
|
f731ee |
+};
|
|
|
f731ee |
+typedef enum grub_efi_ip6_config_data_type grub_efi_ip6_config_data_type_t;
|
|
|
f731ee |
+
|
|
|
f731ee |
+struct grub_efi_ip6_config_protocol
|
|
|
f731ee |
+{
|
|
|
f731ee |
+ grub_efi_status_t (*set_data) (struct grub_efi_ip6_config_protocol *this,
|
|
|
f731ee |
+ grub_efi_ip6_config_data_type_t data_type,
|
|
|
f731ee |
+ grub_efi_uintn_t data_size,
|
|
|
f731ee |
+ void *data);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ grub_efi_status_t (*get_data) (struct grub_efi_ip6_config_protocol *this,
|
|
|
f731ee |
+ grub_efi_ip6_config_data_type_t data_type,
|
|
|
f731ee |
+ grub_efi_uintn_t *data_size,
|
|
|
f731ee |
+ void *data);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ grub_efi_status_t (*register_data_notify) (struct grub_efi_ip6_config_protocol *this,
|
|
|
f731ee |
+ grub_efi_ip6_config_data_type_t data_type,
|
|
|
f731ee |
+ grub_efi_event_t event);
|
|
|
f731ee |
+
|
|
|
f731ee |
+ grub_efi_status_t (*unregister_datanotify) (struct grub_efi_ip6_config_protocol *this,
|
|
|
f731ee |
+ grub_efi_ip6_config_data_type_t data_type,
|
|
|
f731ee |
+ grub_efi_event_t event);
|
|
|
f731ee |
+};
|
|
|
f731ee |
+typedef struct grub_efi_ip6_config_protocol grub_efi_ip6_config_protocol_t;
|
|
|
f731ee |
+
|
|
|
f731ee |
#if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \
|
|
|
f731ee |
|| defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__)
|
|
|
f731ee |
|