[glibc/f20] Resolves: #1047979

Siddhesh Poyarekar siddhesh at fedoraproject.org
Thu Jan 23 10:23:17 UTC 2014


commit f40ae7b7fc76ac11ec049166ff0b59e91a974cd0
Author: Siddhesh Poyarekar <siddhesh at redhat.com>
Date:   Thu Jan 23 15:49:14 2014 +0530

    Resolves: #1047979
    
    Use first name entry for address in /etc/hosts as the canonical name
    in getaddrinfo.

 glibc-rh1047979.patch |  555 +++++++++++++++++++++++++++++++++++++++++++++++++
 glibc.spec            |    8 +-
 2 files changed, 562 insertions(+), 1 deletions(-)
---
diff --git a/glibc-rh1047979.patch b/glibc-rh1047979.patch
new file mode 100644
index 0000000..695da21
--- /dev/null
+++ b/glibc-rh1047979.patch
@@ -0,0 +1,555 @@
+commit 5a4c6d53f50b264d60cf6453576ca2810c7890b7
+Author: Siddhesh Poyarekar <siddhesh at redhat.com>
+Date:   Thu Nov 28 17:18:12 2013 +0530
+
+    Get canonical name in getaddrinfo from hosts file for AF_INET (fixes 16077)
+    
+    AF_INET lookup in hosts file uses _nss_files_gethostbyname2_r, which
+    is not capable of returning a canonical name if it has found one.
+    This change adds _nss_files_gethostbyname3_r, which wraps around
+    _nss_files_gethostbyname2_r and then returns result.h_name as the
+    canonical name.
+
+diff --git a/nss/Versions b/nss/Versions
+index d13d570..f8ababc 100644
+--- a/nss/Versions
++++ b/nss/Versions
+@@ -40,6 +40,7 @@ libnss_files {
+     _nss_files_endhostent;
+     _nss_files_gethostbyaddr_r;
+     _nss_files_gethostbyname2_r;
++    _nss_files_gethostbyname3_r;
+     _nss_files_gethostbyname4_r;
+     _nss_files_gethostbyname_r;
+     _nss_files_gethostent_r;
+diff --git a/nss/nss_files/files-hosts.c b/nss/nss_files/files-hosts.c
+index 6db2535..957c9aa 100644
+--- a/nss/nss_files/files-hosts.c
++++ b/nss/nss_files/files-hosts.c
+@@ -97,262 +97,12 @@ LINE_PARSER
+    STRING_FIELD (result->h_name, isspace, 1);
+  })
+ 
+-
+-
+-#define HOST_DB_LOOKUP(name, keysize, keypattern, break_if_match, proto...) \
+-enum nss_status								      \
+-_nss_files_get##name##_r (proto,					      \
+-			  struct STRUCTURE *result, char *buffer,	      \
+-			  size_t buflen, int *errnop H_ERRNO_PROTO)	      \
+-{									      \
+-  uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);    \
+-  buffer += pad;							      \
+-  buflen = buflen > pad ? buflen - pad : 0;				      \
+-									      \
+-  __libc_lock_lock (lock);						      \
+-									      \
+-  /* Reset file pointer to beginning or open file.  */			      \
+-  enum nss_status status = internal_setent (keep_stream);		      \
+-									      \
+-  if (status == NSS_STATUS_SUCCESS)					      \
+-    {									      \
+-      /* Tell getent function that we have repositioned the file pointer.  */ \
+-      last_use = getby;							      \
+-									      \
+-      while ((status = internal_getent (result, buffer, buflen, errnop	      \
+-					H_ERRNO_ARG EXTRA_ARGS_VALUE))	      \
+-	     == NSS_STATUS_SUCCESS)					      \
+-	{ break_if_match }						      \
+-									      \
+-      if (status == NSS_STATUS_SUCCESS					      \
+-	  && _res_hconf.flags & HCONF_FLAG_MULTI)			      \
+-	{								      \
+-	  /* We have to get all host entries from the file.  */		      \
+-	  size_t tmp_buflen = MIN (buflen, 4096);			      \
+-	  char tmp_buffer_stack[tmp_buflen]				      \
+-	    __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));\
+-	  char *tmp_buffer = tmp_buffer_stack;				      \
+-	  struct hostent tmp_result_buf;				      \
+-	  int naddrs = 1;						      \
+-	  int naliases = 0;						      \
+-	  char *bufferend;						      \
+-	  bool tmp_buffer_malloced = false;				      \
+-									      \
+-	  while (result->h_aliases[naliases] != NULL)			      \
+-	    ++naliases;							      \
+-									      \
+-	  bufferend = (char *) &result->h_aliases[naliases + 1];	      \
+-									      \
+-	again:								      \
+-	  while ((status = internal_getent (&tmp_result_buf, tmp_buffer,      \
+-					    tmp_buflen, errnop H_ERRNO_ARG    \
+-					    EXTRA_ARGS_VALUE))		      \
+-		 == NSS_STATUS_SUCCESS)					      \
+-	    {								      \
+-	      int matches = 1;						      \
+-	      struct hostent *old_result = result;			      \
+-	      result = &tmp_result_buf;					      \
+-	      /* The following piece is a bit clumsy but we want to use the   \
+-		 `break_if_match' value.  The optimizer should do its	      \
+-		 job.  */						      \
+-	      do							      \
+-		{							      \
+-		  break_if_match					      \
+-		  result = old_result;					      \
+-		}							      \
+-	      while ((matches = 0));					      \
+-									      \
+-	      if (matches)						      \
+-		{							      \
+-		  /* We could be very clever and try to recycle a few bytes   \
+-		     in the buffer instead of generating new arrays.  But     \
+-		     we are not doing this here since it's more work than     \
+-		     it's worth.  Simply let the user provide a bit bigger    \
+-		     buffer.  */					      \
+-		  char **new_h_addr_list;				      \
+-		  char **new_h_aliases;					      \
+-		  int newaliases = 0;					      \
+-		  size_t newstrlen = 0;					      \
+-		  int cnt;						      \
+-									      \
+-		  /* Count the new aliases and the length of the strings.  */ \
+-		  while (tmp_result_buf.h_aliases[newaliases] != NULL)	      \
+-		    {							      \
+-		      char *cp = tmp_result_buf.h_aliases[newaliases];	      \
+-		      ++newaliases;					      \
+-		      newstrlen += strlen (cp) + 1;			      \
+-		    }							      \
+-		  /* If the real name is different add it also to the	      \
+-		     aliases.  This means that there is a duplication	      \
+-		     in the alias list but this is really the user's	      \
+-		     problem.  */					      \
+-		  if (strcmp (old_result->h_name,			      \
+-			      tmp_result_buf.h_name) != 0)		      \
+-		    {							      \
+-		      ++newaliases;					      \
+-		      newstrlen += strlen (tmp_result_buf.h_name) + 1;	      \
+-		    }							      \
+-									      \
+-		  /* Make sure bufferend is aligned.  */		      \
+-		  assert ((bufferend - (char *) 0) % sizeof (char *) == 0);   \
+-									      \
+-		  /* Now we can check whether the buffer is large enough.     \
+-		     16 is the maximal size of the IP address.  */	      \
+-		  if (bufferend + 16 + (naddrs + 2) * sizeof (char *)	      \
+-		      + roundup (newstrlen, sizeof (char *))		      \
+-		      + (naliases + newaliases + 1) * sizeof (char *)	      \
+-		      >= buffer + buflen)				      \
+-		    {							      \
+-		      *errnop = ERANGE;					      \
+-		      *herrnop = NETDB_INTERNAL;			      \
+-		      status = NSS_STATUS_TRYAGAIN;			      \
+-		      goto out;						      \
+-		    }							      \
+-									      \
+-		  new_h_addr_list =					      \
+-		    (char **) (bufferend				      \
+-			       + roundup (newstrlen, sizeof (char *))	      \
+-			       + 16);					      \
+-		  new_h_aliases =					      \
+-		    (char **) ((char *) new_h_addr_list			      \
+-			       + (naddrs + 2) * sizeof (char *));	      \
+-									      \
+-		  /* Copy the old data in the new arrays.  */		      \
+-		  for (cnt = 0; cnt < naddrs; ++cnt)			      \
+-		    new_h_addr_list[cnt] = old_result->h_addr_list[cnt];      \
+-									      \
+-		  for (cnt = 0; cnt < naliases; ++cnt)			      \
+-		    new_h_aliases[cnt] = old_result->h_aliases[cnt];	      \
+-									      \
+-		  /* Store the new strings.  */				      \
+-		  cnt = 0;						      \
+-		  while (tmp_result_buf.h_aliases[cnt] != NULL)		      \
+-		    {							      \
+-		      new_h_aliases[naliases++] = bufferend;		      \
+-		      bufferend = (__stpcpy (bufferend,			      \
+-					     tmp_result_buf.h_aliases[cnt])   \
+-				   + 1);				      \
+-		      ++cnt;						      \
+-		    }							      \
+-									      \
+-		  if (cnt < newaliases)					      \
+-		    {							      \
+-		      new_h_aliases[naliases++] = bufferend;		      \
+-		      bufferend = __stpcpy (bufferend,			      \
+-					    tmp_result_buf.h_name) + 1;	      \
+-		    }							      \
+-									      \
+-		  /* Final NULL pointer.  */				      \
+-		  new_h_aliases[naliases] = NULL;			      \
+-									      \
+-		  /* Round up the buffer end address.  */		      \
+-		  bufferend += (sizeof (char *)				      \
+-				- ((bufferend - (char *) 0)		      \
+-				   % sizeof (char *))) % sizeof (char *);     \
+-									      \
+-		  /* Now the new address.  */				      \
+-		  new_h_addr_list[naddrs++] =				      \
+-		    memcpy (bufferend, tmp_result_buf.h_addr,		      \
+-			    tmp_result_buf.h_length);			      \
+-									      \
+-		  /* Also here a final NULL pointer.  */		      \
+-		  new_h_addr_list[naddrs] = NULL;			      \
+-									      \
+-		  /* Store the new array pointers.  */			      \
+-		  old_result->h_aliases = new_h_aliases;		      \
+-		  old_result->h_addr_list = new_h_addr_list;		      \
+-									      \
+-		  /* Compute the new buffer end.  */			      \
+-		  bufferend = (char *) &new_h_aliases[naliases + 1];	      \
+-		  assert (bufferend <= buffer + buflen);		      \
+-									      \
+-		  result = old_result;					      \
+-		}							      \
+-	    }								      \
+-									      \
+-	  if (status == NSS_STATUS_TRYAGAIN)				      \
+-	    {								      \
+-	      size_t newsize = 2 * tmp_buflen;				      \
+-	      if (tmp_buffer_malloced)					      \
+-		{							      \
+-		  char *newp = realloc (tmp_buffer, newsize);		      \
+-		  if (newp != NULL)					      \
+-		    {							      \
+-		      assert ((((uintptr_t) newp)			      \
+-			       & (__alignof__ (struct hostent_data) - 1))     \
+-			      == 0);					      \
+-		      tmp_buffer = newp;				      \
+-		      tmp_buflen = newsize;				      \
+-		      goto again;					      \
+-		    }							      \
+-		}							      \
+-	      else if (!__libc_use_alloca (buflen + newsize))		      \
+-		{							      \
+-		  tmp_buffer = malloc (newsize);			      \
+-		  if (tmp_buffer != NULL)				      \
+-		    {							      \
+-		      assert ((((uintptr_t) tmp_buffer)			      \
+-			       & (__alignof__ (struct hostent_data) - 1))     \
+-			      == 0);					      \
+-		      tmp_buffer_malloced = true;			      \
+-		      tmp_buflen = newsize;				      \
+-		      goto again;					      \
+-		    }							      \
+-		}							      \
+-	      else							      \
+-		{							      \
+-		  tmp_buffer						      \
+-		    = extend_alloca (tmp_buffer, tmp_buflen,		      \
+-				     newsize				      \
+-				     + __alignof__ (struct hostent_data));    \
+-		  tmp_buffer = (char *) (((uintptr_t) tmp_buffer	      \
+-					  + __alignof__ (struct hostent_data) \
+-					  - 1)				      \
+-					 & ~(__alignof__ (struct hostent_data)\
+-					     - 1));			      \
+-		  goto again;						      \
+-		}							      \
+-	    }								      \
+-	  else								      \
+-	    status = NSS_STATUS_SUCCESS;				      \
+-	out:								      \
+-	  if (tmp_buffer_malloced)					      \
+-	    free (tmp_buffer);						      \
+-	}								      \
+-									      \
+-									      \
+-      if (! keep_stream)						      \
+-	internal_endent ();						      \
+-    }									      \
+-									      \
+-  __libc_lock_unlock (lock);						      \
+-									      \
+-  return status;							      \
+-}
+-
+-
+ #define EXTRA_ARGS_VALUE \
+   , ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET),		      \
+   ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
+ #include "files-XXX.c"
+-HOST_DB_LOOKUP (hostbyname, ,,
+-		{
+-		  LOOKUP_NAME_CASE (h_name, h_aliases)
+-		}, const char *name)
+ #undef EXTRA_ARGS_VALUE
+ 
+-
+-/* XXX Is using _res to determine whether we want to convert IPv4 addresses
+-   to IPv6 addresses really the right thing to do?  */
+-#define EXTRA_ARGS_VALUE \
+-  , af, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
+-HOST_DB_LOOKUP (hostbyname2, ,,
+-		{
+-		  LOOKUP_NAME_CASE (h_name, h_aliases)
+-		}, const char *name, int af)
+-#undef EXTRA_ARGS_VALUE
+-
+-
+ /* We only need to consider IPv4 mapped addresses if the input to the
+    gethostbyaddr() function is an IPv6 address.  */
+ #define EXTRA_ARGS_VALUE \
+@@ -365,6 +115,263 @@ DB_LOOKUP (hostbyaddr, ,,,
+ 	   }, const void *addr, socklen_t len, int af)
+ #undef EXTRA_ARGS_VALUE
+ 
++enum nss_status
++_nss_files_gethostbyname3_r (const char *name, int af, struct hostent *result,
++			     char *buffer, size_t buflen, int *errnop,
++			     int *herrnop, int32_t *ttlp, char **canonp)
++{
++  uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct hostent_data);
++  buffer += pad;
++  buflen = buflen > pad ? buflen - pad : 0;
++
++  __libc_lock_lock (lock);
++
++  /* Reset file pointer to beginning or open file.  */
++  enum nss_status status = internal_setent (keep_stream);
++
++  if (status == NSS_STATUS_SUCCESS)
++    {
++      /* XXX Is using _res to determine whether we want to convert IPv4
++         addresses to IPv6 addresses really the right thing to do?  */
++      int flags = ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0);
++
++      /* Tell getent function that we have repositioned the file pointer.  */
++      last_use = getby;
++
++      while ((status = internal_getent (result, buffer, buflen, errnop,
++					herrnop, af, flags))
++	     == NSS_STATUS_SUCCESS)
++	{
++	  LOOKUP_NAME_CASE (h_name, h_aliases)
++	}
++
++      if (status == NSS_STATUS_SUCCESS
++	  && _res_hconf.flags & HCONF_FLAG_MULTI)
++	{
++	  /* We have to get all host entries from the file.  */
++	  size_t tmp_buflen = MIN (buflen, 4096);
++	  char tmp_buffer_stack[tmp_buflen]
++	    __attribute__ ((__aligned__ (__alignof__ (struct hostent_data))));
++	  char *tmp_buffer = tmp_buffer_stack;
++	  struct hostent tmp_result_buf;
++	  int naddrs = 1;
++	  int naliases = 0;
++	  char *bufferend;
++	  bool tmp_buffer_malloced = false;
++
++	  while (result->h_aliases[naliases] != NULL)
++	    ++naliases;
++
++	  bufferend = (char *) &result->h_aliases[naliases + 1];
++
++	again:
++	  while ((status = internal_getent (&tmp_result_buf, tmp_buffer,
++					    tmp_buflen, errnop, herrnop, af,
++					    flags))
++		 == NSS_STATUS_SUCCESS)
++	    {
++	      int matches = 1;
++	      struct hostent *old_result = result;
++	      result = &tmp_result_buf;
++	      /* The following piece is a bit clumsy but we want to use the
++		 `LOOKUP_NAME_CASE' value.  The optimizer should do its
++		 job.  */
++	      do
++		{
++		  LOOKUP_NAME_CASE (h_name, h_aliases)
++		  result = old_result;
++		}
++	      while ((matches = 0));
++
++	      if (matches)
++		{
++		  /* We could be very clever and try to recycle a few bytes
++		     in the buffer instead of generating new arrays.  But
++		     we are not doing this here since it's more work than
++		     it's worth.  Simply let the user provide a bit bigger
++		     buffer.  */
++		  char **new_h_addr_list;
++		  char **new_h_aliases;
++		  int newaliases = 0;
++		  size_t newstrlen = 0;
++		  int cnt;
++
++		  /* Count the new aliases and the length of the strings.  */
++		  while (tmp_result_buf.h_aliases[newaliases] != NULL)
++		    {
++		      char *cp = tmp_result_buf.h_aliases[newaliases];
++		      ++newaliases;
++		      newstrlen += strlen (cp) + 1;
++		    }
++		  /* If the real name is different add it also to the
++		     aliases.  This means that there is a duplication
++		     in the alias list but this is really the user's
++		     problem.  */
++		  if (strcmp (old_result->h_name,
++			      tmp_result_buf.h_name) != 0)
++		    {
++		      ++newaliases;
++		      newstrlen += strlen (tmp_result_buf.h_name) + 1;
++		    }
++
++		  /* Make sure bufferend is aligned.  */
++		  assert ((bufferend - (char *) 0) % sizeof (char *) == 0);
++
++		  /* Now we can check whether the buffer is large enough.
++		     16 is the maximal size of the IP address.  */
++		  if (bufferend + 16 + (naddrs + 2) * sizeof (char *)
++		      + roundup (newstrlen, sizeof (char *))
++		      + (naliases + newaliases + 1) * sizeof (char *)
++		      >= buffer + buflen)
++		    {
++		      *errnop = ERANGE;
++		      *herrnop = NETDB_INTERNAL;
++		      status = NSS_STATUS_TRYAGAIN;
++		      goto out;
++		    }
++
++		  new_h_addr_list =
++		    (char **) (bufferend
++			       + roundup (newstrlen, sizeof (char *))
++			       + 16);
++		  new_h_aliases =
++		    (char **) ((char *) new_h_addr_list
++			       + (naddrs + 2) * sizeof (char *));
++
++		  /* Copy the old data in the new arrays.  */
++		  for (cnt = 0; cnt < naddrs; ++cnt)
++		    new_h_addr_list[cnt] = old_result->h_addr_list[cnt];
++
++		  for (cnt = 0; cnt < naliases; ++cnt)
++		    new_h_aliases[cnt] = old_result->h_aliases[cnt];
++
++		  /* Store the new strings.  */
++		  cnt = 0;
++		  while (tmp_result_buf.h_aliases[cnt] != NULL)
++		    {
++		      new_h_aliases[naliases++] = bufferend;
++		      bufferend = (__stpcpy (bufferend,
++					     tmp_result_buf.h_aliases[cnt])
++				   + 1);
++		      ++cnt;
++		    }
++
++		  if (cnt < newaliases)
++		    {
++		      new_h_aliases[naliases++] = bufferend;
++		      bufferend = __stpcpy (bufferend,
++					    tmp_result_buf.h_name) + 1;
++		    }
++
++		  /* Final NULL pointer.  */
++		  new_h_aliases[naliases] = NULL;
++
++		  /* Round up the buffer end address.  */
++		  bufferend += (sizeof (char *)
++				- ((bufferend - (char *) 0)
++				   % sizeof (char *))) % sizeof (char *);
++
++		  /* Now the new address.  */
++		  new_h_addr_list[naddrs++] =
++		    memcpy (bufferend, tmp_result_buf.h_addr,
++			    tmp_result_buf.h_length);
++
++		  /* Also here a final NULL pointer.  */
++		  new_h_addr_list[naddrs] = NULL;
++
++		  /* Store the new array pointers.  */
++		  old_result->h_aliases = new_h_aliases;
++		  old_result->h_addr_list = new_h_addr_list;
++
++		  /* Compute the new buffer end.  */
++		  bufferend = (char *) &new_h_aliases[naliases + 1];
++		  assert (bufferend <= buffer + buflen);
++
++		  result = old_result;
++		}
++	    }
++
++	  if (status == NSS_STATUS_TRYAGAIN)
++	    {
++	      size_t newsize = 2 * tmp_buflen;
++	      if (tmp_buffer_malloced)
++		{
++		  char *newp = realloc (tmp_buffer, newsize);
++		  if (newp != NULL)
++		    {
++		      assert ((((uintptr_t) newp)
++			       & (__alignof__ (struct hostent_data) - 1))
++			      == 0);
++		      tmp_buffer = newp;
++		      tmp_buflen = newsize;
++		      goto again;
++		    }
++		}
++	      else if (!__libc_use_alloca (buflen + newsize))
++		{
++		  tmp_buffer = malloc (newsize);
++		  if (tmp_buffer != NULL)
++		    {
++		      assert ((((uintptr_t) tmp_buffer)
++			       & (__alignof__ (struct hostent_data) - 1))
++			      == 0);
++		      tmp_buffer_malloced = true;
++		      tmp_buflen = newsize;
++		      goto again;
++		    }
++		}
++	      else
++		{
++		  tmp_buffer
++		    = extend_alloca (tmp_buffer, tmp_buflen,
++				     newsize
++				     + __alignof__ (struct hostent_data));
++		  tmp_buffer = (char *) (((uintptr_t) tmp_buffer
++					  + __alignof__ (struct hostent_data)
++					  - 1)
++					 & ~(__alignof__ (struct hostent_data)
++					     - 1));
++		  goto again;
++		}
++	    }
++	  else
++	    status = NSS_STATUS_SUCCESS;
++	out:
++	  if (tmp_buffer_malloced)
++	    free (tmp_buffer);
++	}
++
++      if (! keep_stream)
++	internal_endent ();
++    }
++
++  if (canonp && status == NSS_STATUS_SUCCESS)
++    *canonp = result->h_name;
++
++  __libc_lock_unlock (lock);
++
++  return status;
++}
++
++enum nss_status
++_nss_files_gethostbyname_r (const char *name, struct hostent *result,
++			    char *buffer, size_t buflen, int *errnop,
++			    int *herrnop)
++{
++  int af = ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET);
++
++  return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
++				      errnop, herrnop, NULL, NULL);
++}
++
++enum nss_status
++_nss_files_gethostbyname2_r (const char *name, int af, struct hostent *result,
++			     char *buffer, size_t buflen, int *errnop,
++			     int *herrnop)
++{
++  return _nss_files_gethostbyname3_r (name, af, result, buffer, buflen,
++				      errnop, herrnop, NULL, NULL);
++}
+ 
+ enum nss_status
+ _nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
diff --git a/glibc.spec b/glibc.spec
index 3e36ec2..c1d2e0c 100644
--- a/glibc.spec
+++ b/glibc.spec
@@ -1,6 +1,6 @@
 %define glibcsrcdir glibc-2.18
 %define glibcversion 2.18
-%define glibcrelease 11%{?dist}
+%define glibcrelease 12%{?dist}
 # Pre-release tarballs are pulled in from git using a command that is
 # effectively:
 #
@@ -189,6 +189,7 @@ Patch0043: %{name}-rh1009145.patch
 #
 Patch1001: %{name}-rh995841.patch
 Patch1002: %{name}-rh1008299.patch
+Patch1003: %{name}-rh1047979.patch
 
 #
 # Patches submitted, but not yet approved upstream.
@@ -564,6 +565,7 @@ package or when debugging this package.
 %patch2031 -p1
 %patch2032 -p1
 %patch0043 -p1
+%patch1003 -p1
 
 ##############################################################################
 # %%prep - Additional prep required...
@@ -1649,6 +1651,10 @@ rm -f *.filelist*
 %endif
 
 %changelog
+* Thu Jan 23 2014 Siddhesh Poyarekar <siddhesh at redhat.com> - 2.18-12
+- Use first name entry for address in /etc/hosts as the canonical name
+  in getaddrinfo (#1047979).
+
 * Wed Oct  2 2013 Carlos O'Donell <carlos at redhat.com> - 2.18-11
 - Allow ldconfig cached objects previously marked as hard or soft
   ABI to now become unmarked without raising an error. This works


More information about the scm-commits mailing list