Exemple #1
0
static int
internal_function
__internal_setnetgrent_reuse (const char *group, struct __netgrent *datap,
			      int *errnop)
{
  union
  {
    enum nss_status (*f) (const char *, struct __netgrent *);
    void *ptr;
  } fct;
  enum nss_status status = NSS_STATUS_UNAVAIL;
  struct name_list *new_elem;

  /* Free data from previous service.  */
  endnetgrent_hook (datap);

  /* Cycle through all the services and run their setnetgrent functions.  */
  int no_more = setup (&fct.ptr, &datap->nip);
  while (! no_more)
    {
      assert (datap->data == NULL);

      /* Ignore status, we force check in `__nss_next2'.  */
      status = DL_CALL_FCT (*fct.f, (group, datap));

      service_user *old_nip = datap->nip;
      no_more = __nss_next2 (&datap->nip, "setnetgrent", NULL, &fct.ptr,
			     status, 0);

      if (status == NSS_STATUS_SUCCESS && ! no_more)
	{
	  enum nss_status (*endfct) (struct __netgrent *);

	  endfct = __nss_lookup_function (old_nip, "endnetgrent");
	  if (endfct != NULL)
	    (void) DL_CALL_FCT (*endfct, (datap));
	}
    }

  /* Add the current group to the list of known groups.  */
  size_t group_len = strlen (group) + 1;
  new_elem = (struct name_list *) malloc (sizeof (struct name_list)
					  + group_len);
  if (new_elem == NULL)
    {
      *errnop = errno;
      status = NSS_STATUS_TRYAGAIN;
    }
  else
    {
      new_elem->next = datap->known_groups;
      memcpy (new_elem->name, group, group_len);
      datap->known_groups = new_elem;
    }

  return status == NSS_STATUS_SUCCESS;
}
int
internal_function
__gconv (__gconv_t cd, const unsigned char **inbuf,
	 const unsigned char *inbufend, unsigned char **outbuf,
	 unsigned char *outbufend, size_t *irreversible)
{
  size_t last_step;
  int result;

  if (cd == (__gconv_t) -1L)
    return __GCONV_ILLEGAL_DESCRIPTOR;

  last_step = cd->__nsteps - 1;

  assert (irreversible != NULL);
  *irreversible = 0;

  cd->__data[last_step].__outbuf = outbuf != NULL ? *outbuf : NULL;
  cd->__data[last_step].__outbufend = outbufend;

  if (inbuf == NULL || *inbuf == NULL)
    /* We just flush.  */
    result = DL_CALL_FCT (cd->__steps->__fct,
			  (cd->__steps, cd->__data, NULL, NULL, NULL,
			   irreversible,
			   cd->__data[last_step].__outbuf == NULL ? 2 : 1, 0));
  else
    {
      const unsigned char *last_start;

      assert (outbuf != NULL && *outbuf != NULL);

      do
	{
	  last_start = *inbuf;
	  result = DL_CALL_FCT (cd->__steps->__fct,
				(cd->__steps, cd->__data, inbuf, inbufend,
				 NULL, irreversible, 0, 0));
	}
      while (result == __GCONV_EMPTY_INPUT && last_start != *inbuf
	     && *inbuf + cd->__steps->__min_needed_from <= inbufend);
    }

  if (outbuf != NULL && *outbuf != NULL)
    *outbuf = cd->__data[last_step].__outbuf;

  return result;
}
Exemple #3
0
FILE *
fopen(
	char const *	fn,
	char const *	mode
)
{
	static Foolie_t	foo;
	FILE *		fyle;

	if( ! foo.f )	{
		foo.v = dlsym( RTLD_NEXT, "fopen" );
		if( ! foo.v )	{
			puts( "not found!" );
			exit( 1 );
		}
		printf( "Real routine is at %p.\n", foo.v );
	}
#if	USE_PROFILING
	fyle = DL_CALL_FCT( foo.f, ( fn, mode ) );
#else	/* USE_PROFILING */
	fyle = foo.f( fn, mode );
#endif	/* USE_PROFILING */
	printf( 
		"Your fopen( '%s' ) %s.\n",
		fn,
		fyle ? "succeeded" : "FAILED"
	);
	return( fyle );
}
Exemple #4
0
static int
internal_function
find_module (const char *directory, const char *filename,
	     struct __gconv_step *result)
{
  size_t dirlen = strlen (directory);
  size_t fnamelen = strlen (filename) + 1;
  char fullname[dirlen + fnamelen];
  int status = __GCONV_NOCONV;

  memcpy (__mempcpy (fullname, directory, dirlen), filename, fnamelen);

  result->__shlib_handle = __gconv_find_shlib (fullname);
  if (result->__shlib_handle != NULL)
    {
      status = __GCONV_OK;

      result->__modname = NULL;
      result->__fct = result->__shlib_handle->fct;
      result->__init_fct = result->__shlib_handle->init_fct;
      result->__end_fct = result->__shlib_handle->end_fct;

      /* These settings can be overridden by the init function.  */
      result->__btowc_fct = NULL;
      result->__data = NULL;

      /* Call the init function.  */
      if (result->__init_fct != NULL)
	status = DL_CALL_FCT (result->__init_fct, (result));
    }

  return status;
}
Exemple #5
0
static int
do_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
	   const char *from_start, const char *from_end, _IO_size_t max)
{
  int result;
#ifdef _LIBC
  const unsigned char *cp = (const unsigned char *) from_start;
  wchar_t to_buf[max];
  struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps;
  size_t dummy;

  codecvt->__cd_in.__cd.__data[0].__outbuf = (unsigned char *) to_buf;
  codecvt->__cd_in.__cd.__data[0].__outbufend = (unsigned char *) &to_buf[max];
  codecvt->__cd_in.__cd.__data[0].__statep = statep;

  __gconv_fct fct = gs->__fct;
#ifdef PTR_DEMANGLE
  if (gs->__shlib_handle != NULL)
    PTR_DEMANGLE (fct);
#endif

  DL_CALL_FCT (fct,
	       (gs, codecvt->__cd_in.__cd.__data, &cp,
		(const unsigned char *) from_end, NULL,
		&dummy, 0, 0));

  result = cp - (const unsigned char *) from_start;
#else
# ifdef _GLIBCPP_USE_WCHAR_T
  const char *from_start_copy = (const char *) from_start;
  size_t from_len = from_end - from_start;
  wchar_t to_buf[max];
  size_t res;
  char *to_start = (char *) to_buf;

  res = iconv (codecvt->__cd_in, &from_start_copy, &from_len,
	       &to_start, &max);

  result = from_start_copy - (char *) from_start;
# else
  /* Decide what to do.  */
  result = 0;
# endif
#endif

  return result;
}
size_t
__mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
{
  wchar_t buf[1];
  struct __gconv_step_data data;
  int status;
  size_t result;
  size_t dummy;
  const unsigned char *inbuf;
  char *outbuf = (char *) (pwc ?: buf);

  /* Set information for this step.  */
  data.__invocation_counter = 0;
  data.__internal_use = 1;
  data.__flags = __GCONV_IS_LAST;
  data.__statep = ps ?: &state;
  data.__trans = NULL;

  /* A first special case is if S is NULL.  This means put PS in the
     initial state.  */
  if (s == NULL)
    {
      outbuf = (char *) buf;
      s = "";
      n = 1;
    }

  /* Tell where we want the result.  */
  data.__outbuf = outbuf;
  data.__outbufend = outbuf + sizeof (wchar_t);

  /* Make sure we use the correct function.  */
  update_conversion_ptrs ();

  /* Do a normal conversion.  */
  inbuf = (const unsigned char *) s;
  status = DL_CALL_FCT (__wcsmbs_gconv_fcts.towc->__fct,
			(__wcsmbs_gconv_fcts.towc, &data, &inbuf, inbuf + n,
			 NULL, &dummy, 0, 1));

  /* There must not be any problems with the conversion but illegal input
     characters.  The output buffer must be large enough, otherwise the
     definition of MB_CUR_MAX is not correct.  All the other possible
     errors also must not happen.  */
  assert (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT
	  || status == __GCONV_ILLEGAL_INPUT
	  || status == __GCONV_INCOMPLETE_INPUT
	  || status == __GCONV_FULL_OUTPUT);

  if (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT
      || status == __GCONV_FULL_OUTPUT)
    {
      if (data.__outbuf != (unsigned char *) outbuf
	  && *(wchar_t *) outbuf == L'\0')
	{
	  /* The converted character is the NUL character.  */
	  assert (__mbsinit (data.__statep));
	  result = 0;
	}
      else
	result = inbuf - (const unsigned char *) s;
    }
  else if (status == __GCONV_INCOMPLETE_INPUT)
    result = (size_t) -2;
  else
    {
      result = (size_t) -1;
      __set_errno (EILSEQ);
    }

  return result;
}
Exemple #7
0
int
__gconv_transliterate (struct __gconv_step *step,
		       struct __gconv_step_data *step_data,
		       const unsigned char *inbufstart,
		       const unsigned char **inbufp,
		       const unsigned char *inbufend,
		       unsigned char **outbufstart, size_t *irreversible)
{
  /* Find out about the locale's transliteration.  */
  uint_fast32_t size;
  const uint32_t *from_idx;
  const uint32_t *from_tbl;
  const uint32_t *to_idx;
  const uint32_t *to_tbl;
  const uint32_t *winbuf;
  const uint32_t *winbufend;
  uint_fast32_t low;
  uint_fast32_t high;

  /* The input buffer.  There are actually 4-byte values.  */
  winbuf = (const uint32_t *) *inbufp;
  winbufend = (const uint32_t *) inbufend;

  __gconv_fct fct = step->__fct;
#ifdef PTR_DEMANGLE
  if (step->__shlib_handle != NULL)
    PTR_DEMANGLE (fct);
#endif

  /* If there is no transliteration information in the locale don't do
     anything and return the error.  */
  size = _NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_TRANSLIT_TAB_SIZE);
  if (size == 0)
    goto no_rules;

  /* Get the rest of the values.  */
  from_idx =
    (const uint32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TRANSLIT_FROM_IDX);
  from_tbl =
    (const uint32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TRANSLIT_FROM_TBL);
  to_idx =
    (const uint32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TRANSLIT_TO_IDX);
  to_tbl =
    (const uint32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TRANSLIT_TO_TBL);

  /* Test whether there is enough input.  */
  if (winbuf + 1 > winbufend)
    return (winbuf == winbufend
	    ? __GCONV_EMPTY_INPUT : __GCONV_INCOMPLETE_INPUT);

  /* The array starting at FROM_IDX contains indeces to the string table
     in FROM_TBL.  The indeces are sorted wrt to the strings.  I.e., we
     are doing binary search.  */
  low = 0;
  high = size;
  while (low < high)
    {
      uint_fast32_t med = (low + high) / 2;
      uint32_t idx;
      int cnt;

      /* Compare the string at this index with the string at the current
	 position in the input buffer.  */
      idx = from_idx[med];
      cnt = 0;
      do
	{
	  if (from_tbl[idx + cnt] != winbuf[cnt])
	    /* Does not match.  */
	    break;
	  ++cnt;
	}
      while (from_tbl[idx + cnt] != L'\0' && winbuf + cnt < winbufend);

      if (cnt > 0 && from_tbl[idx + cnt] == L'\0')
	{
	  /* Found a matching input sequence.  Now try to convert the
	     possible replacements.  */
	  uint32_t idx2 = to_idx[med];

	  do
	    {
	      /* Determine length of replacement.  */
	      uint_fast32_t len = 0;
	      int res;
	      const unsigned char *toinptr;
	      unsigned char *outptr;

	      while (to_tbl[idx2 + len] != L'\0')
		++len;

	      /* Try this input text.  */
	      toinptr = (const unsigned char *) &to_tbl[idx2];
	      outptr = *outbufstart;
	      res = DL_CALL_FCT (fct,
				 (step, step_data, &toinptr,
				  (const unsigned char *) &to_tbl[idx2 + len],
				  &outptr, NULL, 0, 0));
	      if (res != __GCONV_ILLEGAL_INPUT)
		{
		  /* If the conversion succeeds we have to increment the
		     input buffer.  */
		  if (res == __GCONV_EMPTY_INPUT)
		    {
		      *inbufp += cnt * sizeof (uint32_t);
		      ++*irreversible;
		      res = __GCONV_OK;
		    }
		  /* Do not increment the output pointer if we could not
		     store the entire output. */
		  if (res != __GCONV_FULL_OUTPUT)
		    *outbufstart = outptr;

		  return res;
		}

	      /* Next replacement.  */
	      idx2 += len + 1;
	    }
	  while (to_tbl[idx2] != L'\0');

	  /* Nothing found, continue searching.  */
	}
      else if (cnt > 0)
	/* This means that the input buffer contents matches a prefix of
	   an entry.  Since we cannot match it unless we get more input,
	   we will tell the caller about it.  */
	return __GCONV_INCOMPLETE_INPUT;

      if (winbuf + cnt >= winbufend || from_tbl[idx + cnt] < winbuf[cnt])
	low = med + 1;
      else
	high = med;
    }

 no_rules:
  /* Maybe the character is supposed to be ignored.  */
  if (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_TRANSLIT_IGNORE_LEN) != 0)
    {
      int n = _NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_TRANSLIT_IGNORE_LEN);
      const uint32_t *ranges =
	(const uint32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TRANSLIT_IGNORE);
      const uint32_t wc = *(const uint32_t *) (*inbufp);
      int i;

      /* Test whether there is enough input.  */
      if (winbuf + 1 > winbufend)
	return (winbuf == winbufend
		? __GCONV_EMPTY_INPUT : __GCONV_INCOMPLETE_INPUT);

      for (i = 0; i < n; ranges += 3, ++i)
	if (ranges[0] <= wc && wc <= ranges[1]
	    && (wc - ranges[0]) % ranges[2] == 0)
	  {
	    /* Matches the range.  Ignore it.  */
	    *inbufp += 4;
	    ++*irreversible;
	    return __GCONV_OK;
	  }
	else if (wc < ranges[0])
	  /* There cannot be any other matching range since they are
             sorted.  */
	  break;
    }

  /* One last chance: use the default replacement.  */
  if (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_TRANSLIT_DEFAULT_MISSING_LEN) != 0)
    {
      const uint32_t *default_missing = (const uint32_t *)
	_NL_CURRENT (LC_CTYPE, _NL_CTYPE_TRANSLIT_DEFAULT_MISSING);
      const unsigned char *toinptr = (const unsigned char *) default_missing;
      uint32_t len = _NL_CURRENT_WORD (LC_CTYPE,
				       _NL_CTYPE_TRANSLIT_DEFAULT_MISSING_LEN);
      unsigned char *outptr;
      int res;

      /* Test whether there is enough input.  */
      if (winbuf + 1 > winbufend)
	return (winbuf == winbufend
		? __GCONV_EMPTY_INPUT : __GCONV_INCOMPLETE_INPUT);

      outptr = *outbufstart;
      res = DL_CALL_FCT (fct,
			 (step, step_data, &toinptr,
			  (const unsigned char *) (default_missing + len),
			  &outptr, NULL, 0, 0));

      if (res != __GCONV_ILLEGAL_INPUT)
	{
	  /* If the conversion succeeds we have to increment the
	     input buffer.  */
	  if (res == __GCONV_EMPTY_INPUT)
	    {
	      /* This worked but is not reversible.  */
	      ++*irreversible;
	      *inbufp += 4;
	      res = __GCONV_OK;
	    }
	  *outbufstart = outptr;

	  return res;
	}
    }

  /* Haven't found a match.  */
  return __GCONV_ILLEGAL_INPUT;
}
Exemple #8
0
static int
internal_getgrouplist (const char *user, gid_t group, long int *size,
		       gid_t **groupsp, long int limit)
{
#ifdef USE_NSCD
  if (__nss_not_use_nscd_group > 0
      && ++__nss_not_use_nscd_group > NSS_NSCD_RETRY)
    __nss_not_use_nscd_group = 0;
  if (!__nss_not_use_nscd_group
      && !__nss_database_custom[NSS_DBSIDX_group])
    {
      int n = __nscd_getgrouplist (user, group, size, groupsp, limit);
      if (n >= 0)
	return n;

      /* nscd is not usable.  */
      __nss_not_use_nscd_group = 1;
    }
#endif

  enum nss_status status = NSS_STATUS_UNAVAIL;
  int no_more = 0;

  /* Never store more than the starting *SIZE number of elements.  */
  assert (*size > 0);
  (*groupsp)[0] = group;
  /* Start is one, because we have the first group as parameter.  */
  long int start = 1;

  if (__nss_initgroups_database == NULL)
    {
      if (__nss_database_lookup ("initgroups", NULL, "",
				 &__nss_initgroups_database) < 0)
	{
	  if (__nss_group_database == NULL)
	    no_more = __nss_database_lookup ("group", NULL, "compat files",
					     &__nss_group_database);

	  __nss_initgroups_database = __nss_group_database;
	}
      else
	use_initgroups_entry = true;
    }
  else
    /* __nss_initgroups_database might have been set through
       __nss_configure_lookup in which case use_initgroups_entry was
       not set here.  */
    use_initgroups_entry = __nss_initgroups_database != __nss_group_database;

  service_user *nip = __nss_initgroups_database;
  while (! no_more)
    {
      long int prev_start = start;

      initgroups_dyn_function fct = __nss_lookup_function (nip,
							   "initgroups_dyn");
      if (fct == NULL)
	status = compat_call (nip, user, group, &start, size, groupsp,
			      limit, &errno);
      else
	status = DL_CALL_FCT (fct, (user, group, &start, size, groupsp,
				    limit, &errno));

      /* Remove duplicates.  */
      long int cnt = prev_start;
      while (cnt < start)
	{
	  long int inner;
	  for (inner = 0; inner < prev_start; ++inner)
	    if ((*groupsp)[inner] == (*groupsp)[cnt])
	      break;

	  if (inner < prev_start)
	    (*groupsp)[cnt] = (*groupsp)[--start];
	  else
	    ++cnt;
	}

      /* This is really only for debugging.  */
      if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
	__libc_fatal ("illegal status in internal_getgrouplist");

      /* For compatibility reason we will continue to look for more
	 entries using the next service even though data has already
	 been found if the nsswitch.conf file contained only a 'groups'
	 line and no 'initgroups' line.  If the latter is available
	 we always respect the status.  This means that the default
	 for successful lookups is to return.  */
      if ((use_initgroups_entry || status != NSS_STATUS_SUCCESS)
	  && nss_next_action (nip, status) == NSS_ACTION_RETURN)
	 break;

      if (nip->next == NULL)
	no_more = -1;
      else
	nip = nip->next;
    }

  return start;
}
size_t
__wcrtomb (char *s, wchar_t wc, mbstate_t *ps)
{
  char buf[MB_CUR_MAX];
  struct __gconv_step_data data;
  int status;
  size_t result;
  size_t dummy;
  const struct gconv_fcts *fcts;

  /* Set information for this step.  */
  data.__invocation_counter = 0;
  data.__internal_use = 1;
  data.__flags = __GCONV_IS_LAST;
  data.__statep = ps ?: &state;
  data.__trans = NULL;

  /* A first special case is if S is NULL.  This means put PS in the
     initial state.  */
  if (s == NULL)
    {
      s = buf;
      wc = L'\0';
    }

  /* Tell where we want to have the result.  */
  data.__outbuf = (unsigned char *) s;
  data.__outbufend = (unsigned char *) s + MB_CUR_MAX;

  /* Get the conversion functions.  */
  fcts = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE));
  __gconv_fct fct = fcts->tomb->__fct;
#ifdef PTR_DEMANGLE
  if (fcts->tomb->__shlib_handle != NULL)
    PTR_DEMANGLE (fct);
#endif

  /* If WC is the NUL character we write into the output buffer the byte
     sequence necessary for PS to get into the initial state, followed
     by a NUL byte.  */
  if (wc == L'\0')
    {
      status = DL_CALL_FCT (fct, (fcts->tomb, &data, NULL, NULL,
				  NULL, &dummy, 1, 1));

      if (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT)
	*data.__outbuf++ = '\0';
    }
  else
    {
      /* Do a normal conversion.  */
      const unsigned char *inbuf = (const unsigned char *) &wc;

      status = DL_CALL_FCT (fct,
			    (fcts->tomb, &data, &inbuf,
			     inbuf + sizeof (wchar_t), NULL, &dummy, 0, 1));
    }

  /* There must not be any problems with the conversion but illegal input
     characters.  The output buffer must be large enough, otherwise the
     definition of MB_CUR_MAX is not correct.  All the other possible
     errors also must not happen.  */
  assert (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT
	  || status == __GCONV_ILLEGAL_INPUT
	  || status == __GCONV_INCOMPLETE_INPUT
	  || status == __GCONV_FULL_OUTPUT);

  if (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT
      || status == __GCONV_FULL_OUTPUT)
    result = data.__outbuf - (unsigned char *) s;
  else
    {
      result = (size_t) -1;
      __set_errno (EILSEQ);
    }

  return result;
}
Exemple #10
0
static enum __codecvt_result
do_in (struct _IO_codecvt *codecvt, __mbstate_t *statep,
       const char *from_start, const char *from_end, const char **from_stop,
       wchar_t *to_start, wchar_t *to_end, wchar_t **to_stop)
{
  enum __codecvt_result result;

#ifdef _LIBC
  struct __gconv_step *gs = codecvt->__cd_in.__cd.__steps;
  int status;
  size_t dummy;
  const unsigned char *from_start_copy = (unsigned char *) from_start;

  codecvt->__cd_in.__cd.__data[0].__outbuf = (unsigned char *) to_start;
  codecvt->__cd_in.__cd.__data[0].__outbufend = (unsigned char *) to_end;
  codecvt->__cd_in.__cd.__data[0].__statep = statep;

  __gconv_fct fct = gs->__fct;
#ifdef PTR_DEMANGLE
  if (gs->__shlib_handle != NULL)
    PTR_DEMANGLE (fct);
#endif

  status = DL_CALL_FCT (fct,
			(gs, codecvt->__cd_in.__cd.__data, &from_start_copy,
			 (const unsigned char *) from_end, NULL,
			 &dummy, 0, 0));

  *from_stop = (const char *) from_start_copy;
  *to_stop = (wchar_t *) codecvt->__cd_in.__cd.__data[0].__outbuf;

  switch (status)
    {
    case __GCONV_OK:
    case __GCONV_EMPTY_INPUT:
      result = __codecvt_ok;
      break;

    case __GCONV_FULL_OUTPUT:
    case __GCONV_INCOMPLETE_INPUT:
      result = __codecvt_partial;
      break;

    default:
      result = __codecvt_error;
      break;
    }
#else
# ifdef _GLIBCPP_USE_WCHAR_T
  size_t res;
  const char *from_start_copy = (const char *) from_start;
  size_t from_len = from_end - from_start;
  char *to_start_copy = (char *) from_start;
  size_t to_len = to_end - to_start;

  res = iconv (codecvt->__cd_in, &from_start_copy, &from_len,
	       &to_start_copy, &to_len);

  if (res == 0)
    result = __codecvt_ok;
  else if (to_len == 0)
    result = __codecvt_partial;
  else if (from_len < codecvt->__codecvt_do_max_length (codecvt))
    result = __codecvt_partial;
  else
    result = __codecvt_error;
# else
  /* Decide what to do.  */
  result = __codecvt_error;
# endif
#endif

  return result;
}
Exemple #11
0
enum nss_status
_nss_nonlocal_setspent(int stayopen)
{
    enum nss_status status;
    const struct walk_nss w = {
	.lookup = &__nss_shadow_nonlocal_lookup, .fct_name = "setspent",
	.status = &status
    };
    const __typeof__(&_nss_nonlocal_setspent) self = NULL;
#define args (stayopen)
#include "walk_nss.h"
#undef args
    if (status != NSS_STATUS_SUCCESS)
	return status;

    if (spent_fct_start == NULL)
	__nss_shadow_nonlocal_lookup(&spent_startp, spent_fct_name,
				     &spent_fct_start);
    spent_nip = spent_startp;
    spent_fct.ptr = spent_fct_start;
    return NSS_STATUS_SUCCESS;
}

enum nss_status
_nss_nonlocal_endspent(void)
{
    enum nss_status status;
    const struct walk_nss w = {
	.lookup = &__nss_shadow_nonlocal_lookup, .fct_name = "endspent",
	.status = &status
    };
    const __typeof__(&_nss_nonlocal_endspent) self = NULL;

    spent_nip = NULL;

#define args ()
#include "walk_nss.h"
#undef args
    return status;
}

enum nss_status
_nss_nonlocal_getspent_r(struct spwd *pwd, char *buffer, size_t buflen,
			 int *errnop)
{
    enum nss_status status;
    if (spent_nip == NULL) {
	status = _nss_nonlocal_setspent(0);
	if (status != NSS_STATUS_SUCCESS)
	    return status;
    }
    do {
	if (spent_fct.ptr == NULL)
	    status = NSS_STATUS_UNAVAIL;
	else
	    status = DL_CALL_FCT(spent_fct.l, (pwd, buffer, buflen, errnop));	
	if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
	    return status;

	if (status == NSS_STATUS_SUCCESS)
	    return NSS_STATUS_SUCCESS;
    } while (__nss_next(&spent_nip, spent_fct_name, &spent_fct.ptr, status, 0) == 0);

    spent_nip = NULL;
    return NSS_STATUS_NOTFOUND;
}


enum nss_status
_nss_nonlocal_getspnam_r(const char *name, struct spwd *pwd,
			 char *buffer, size_t buflen, int *errnop)
{
    enum nss_status status;
    const struct walk_nss w = {
	.lookup = __nss_shadow_nonlocal_lookup, .fct_name = "getspnam_r",
	.status = &status, .errnop = errnop
    };
    const __typeof__(&_nss_nonlocal_getspnam_r) self = NULL;
#define args (name, pwd, buffer, buflen, errnop)
#include "walk_nss.h"
#undef args
    if (status != NSS_STATUS_SUCCESS)
	return status;

    if (strcmp(name, pwd->sp_namp) != 0) {
	syslog(LOG_ERR, "nss_nonlocal: discarding shadow %s from lookup for shadow %s\n", pwd->sp_namp, name);
	return NSS_STATUS_NOTFOUND;
    }

    return NSS_STATUS_SUCCESS;
}
Exemple #12
0
int
internal_function
__internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
			  struct __netgrent *datap,
			  char *buffer, size_t buflen, int *errnop)
{
  enum nss_status (*fct) (struct __netgrent *, char *, size_t, int *);

  /* Initialize status to return if no more functions are found.  */
  enum nss_status status = NSS_STATUS_NOTFOUND;

  /* Run through available functions, starting with the same function last
     run.  We will repeat each function as long as it succeeds, and then go
     on to the next service action.  */
  int no_more = datap->nip == NULL;
  if (! no_more)
    {
#ifdef USE_NSCD
      /* This bogus function pointer is a special marker left by
	 __nscd_setnetgrent to tell us to use the data it left
	 before considering any modules.  */
      if (datap->nip == (service_user *) -1l)
	fct = nscd_getnetgrent;
      else
#endif
	{
	  fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
	  no_more = fct == NULL;
	}

      while (! no_more)
	{
	  status = DL_CALL_FCT (*fct, (datap, buffer, buflen, &errno));

	  if (status == NSS_STATUS_RETURN
	      /* The service returned a NOTFOUND, but there are more groups that
		 we need to resolve before we give up.  */
	      || (status == NSS_STATUS_NOTFOUND && datap->needed_groups != NULL))
	    {
	      /* This was the last one for this group.  Look at next group
		 if available.  */
	      int found = 0;
	      while (datap->needed_groups != NULL && ! found)
		{
		  struct name_list *tmp = datap->needed_groups;
		  datap->needed_groups = datap->needed_groups->next;
		  tmp->next = datap->known_groups;
		  datap->known_groups = tmp;

		  found = __internal_setnetgrent_reuse (datap->known_groups->name,
							datap, errnop);
		}

	      if (found && datap->nip != NULL)
		{
		  fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
		  if (fct != NULL)
		    continue;
		}
	    }
	  else if (status == NSS_STATUS_SUCCESS && datap->type == group_val)
	    {
	      /* The last entry was a name of another netgroup.  */
	      struct name_list *namep;

	      /* Ignore if we've seen the name before.  */
	      for (namep = datap->known_groups; namep != NULL;
		   namep = namep->next)
		if (strcmp (datap->val.group, namep->name) == 0)
		  break;
	      if (namep == NULL)
		for (namep = datap->needed_groups; namep != NULL;
		     namep = namep->next)
		  if (strcmp (datap->val.group, namep->name) == 0)
		    break;
	      if (namep != NULL)
		/* Really ignore.  */
		continue;

	      size_t group_len = strlen (datap->val.group) + 1;
	      namep = (struct name_list *) malloc (sizeof (struct name_list)
						  + group_len);
	      if (namep == NULL)
		/* We are out of memory.  */
		status = NSS_STATUS_RETURN;
	      else
		{
		  namep->next = datap->needed_groups;
		  memcpy (namep->name, datap->val.group, group_len);
		  datap->needed_groups = namep;
		  /* And get the next entry.  */
		  continue;
		}
	    }
	  break;
	}
    }

  if (status == NSS_STATUS_SUCCESS)
    {
      *hostp = (char *) datap->val.triple.host;
      *userp = (char *) datap->val.triple.user;
      *domainp = (char *) datap->val.triple.domain;
    }

  return status == NSS_STATUS_SUCCESS ? 1 : 0;
}
Exemple #13
0
void
query_forward(netresolve_query_t query, char **settings)
{
	const char *node = netresolve_backend_get_nodename(query);
	int family = netresolve_backend_get_family(query);
	struct priv_nss priv = { 0 };

	initialize(&priv, query, settings);

	if (!priv.dl_handle) {
		netresolve_backend_failed(query);
		return;
	}

	if (priv.getaddrinfo) {
		const char *service = netresolve_backend_get_servname(query);
		struct addrinfo hints = netresolve_backend_get_addrinfo_hints(query);
		int status;
		struct addrinfo *result;
		int32_t ttl;

		status = DL_CALL_FCT(priv.getaddrinfo, (NULL, node, service, &hints, &result, &ttl));
		netresolve_backend_apply_addrinfo(query, status, result, ttl);
		if (status == 0)
			freeaddrinfo(result);
	} else if (node && priv.gethostbyname4_r && family == AF_UNSPEC) {
		char buffer[SIZE] = { 0 };
		enum nss_status status;
		/* The libnss_files.so plugin checks the gaih_addrtuple pointer for being
		 * NULL and fails badly otherwise. Whether such behavior is correct
		 * remains a question.
		 */
		struct gaih_addrtuple *result = NULL;
		int errnop, h_errnop;
		int32_t ttl = 0;

		/* Without this, libnss_files won't resolve using multiple records
		 * in /etc/hosts, e.g. won't return both IPv4 and IPv6 for "localhost"
		 * even when /etc/hosts is configured correctly.
		 *
		 * See relevant files in glibc sources:
		 *  - nss_files/files-hosts.c
		 *  - resolv/res_hconf.h
		 */
		extern struct {
			int initialized;
			int unused1;
			int unused2[4];
			int num_trimdomains;
			const char *trimdomain[4];
			unsigned int flags;
		} _res_hconf;
		_res_hconf.flags = 0x10;

		status = DL_CALL_FCT(priv.gethostbyname4_r, (node, &result,
			buffer, sizeof buffer, &errnop, &h_errnop, &ttl));
		netresolve_backend_apply_addrtuple(query, status, result, ttl);
	} else if (node && (priv.gethostbyname3_r || priv.gethostbyname2_r)) {
		char buffer4[SIZE] = { 0 };
		char buffer6[SIZE] = { 0 };
		int status4 = NSS_STATUS_NOTFOUND, status6 = NSS_STATUS_NOTFOUND;
		struct hostent he4, he6;
		int errnop, h_errnop;
		int32_t ttl4 = 0;
		int32_t ttl6 = 0;
		char *canonname4 = NULL;
		char *canonname6 = NULL;

		if (priv.gethostbyname3_r) {
			if (family == AF_INET || family == AF_UNSPEC)
				status4 = DL_CALL_FCT(priv.gethostbyname3_r, (node, AF_INET,
					&he4, buffer4, sizeof buffer4, &errnop, &h_errnop, &ttl4, &canonname4));
			if (family == AF_INET6 || family == AF_UNSPEC)
				status6 = DL_CALL_FCT(priv.gethostbyname3_r, (node, AF_INET6,
					&he6, buffer6, sizeof buffer6, &errnop, &h_errnop, &ttl6, &canonname6));
		} else {
			if (family == AF_INET || family == AF_UNSPEC)
				status4 = DL_CALL_FCT(priv.gethostbyname2_r, (node, AF_INET,
					&he4, buffer4, sizeof buffer4, &errnop, &h_errnop));
			if (family == AF_INET6 || family == AF_UNSPEC)
				status6 = DL_CALL_FCT(priv.gethostbyname2_r, (node, AF_INET6,
					&he6, buffer6, sizeof buffer6, &errnop, &h_errnop));
		}

		if (combine_statuses(status4, status6) == NSS_STATUS_SUCCESS) {
			if (status6 == NSS_STATUS_SUCCESS && canonname6)
				netresolve_backend_set_canonical_name(query, canonname6);
			else if (status4 == NSS_STATUS_SUCCESS && canonname4)
				netresolve_backend_set_canonical_name(query, canonname4);
			if (status6 == NSS_STATUS_SUCCESS)
				netresolve_backend_apply_hostent(query, &he6, 0, 0, 0, 0, 0, ttl4);
			if (status4 == NSS_STATUS_SUCCESS)
				netresolve_backend_apply_hostent(query, &he4, 0, 0, 0, 0, 0, ttl6);
			netresolve_backend_finished(query);
		} else
			netresolve_backend_failed(query);
	} else if (node && priv.gethostbyname_r) {
		char buffer[SIZE];
		int errnop, h_errnop;
		struct hostent he;
		enum nss_status status;

		status = DL_CALL_FCT(priv.gethostbyname_r, (node,
			&he, buffer, sizeof buffer, &errnop, &h_errnop));

		if (status == NSS_STATUS_SUCCESS) {
			netresolve_backend_apply_hostent(query, &he, 0, 0, 0, 0, 0, 0);
			netresolve_backend_finished(query);
		} else
			netresolve_backend_failed(query);
	} else {
		debug("no suitable backend found");
		netresolve_backend_failed(query);
	}

	finalize(&priv);
}
Exemple #14
0
int
FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data,
	       const unsigned char **inptrp, const unsigned char *inend,
	       unsigned char **outbufstart, size_t *irreversible, int do_flush,
	       int consume_incomplete)
{
  struct __gconv_step *next_step = step + 1;
  struct __gconv_step_data *next_data = data + 1;
  __gconv_fct fct = NULL;
  int status;

  if ((data->__flags & __GCONV_IS_LAST) == 0)
    {
      fct = next_step->__fct;
#ifdef PTR_DEMANGLE
      if (next_step->__shlib_handle != NULL)
	PTR_DEMANGLE (fct);
#endif
    }

  /* If the function is called with no input this means we have to reset
     to the initial state.  The possibly partly converted input is
     dropped.  */
  if (__builtin_expect (do_flush, 0))
    {
      /* This should never happen during error handling.  */
      assert (outbufstart == NULL);

      status = __GCONV_OK;

#ifdef EMIT_SHIFT_TO_INIT
      if (do_flush == 1)
	{
	  /* We preserve the initial values of the pointer variables.  */
	  unsigned char *outbuf = data->__outbuf;
	  unsigned char *outstart = outbuf;
	  unsigned char *outend = data->__outbufend;

# ifdef PREPARE_LOOP
	  PREPARE_LOOP
# endif

# ifdef SAVE_RESET_STATE
	  SAVE_RESET_STATE (1);
# endif

	  /* Emit the escape sequence to reset the state.  */
	  EMIT_SHIFT_TO_INIT;

	  /* Call the steps down the chain if there are any but only if we
	     successfully emitted the escape sequence.  This should only
	     fail if the output buffer is full.  If the input is invalid
	     it should be discarded since the user wants to start from a
	     clean state.  */
	  if (status == __GCONV_OK)
	    {
	      if (data->__flags & __GCONV_IS_LAST)
		/* Store information about how many bytes are available.  */
		data->__outbuf = outbuf;
	      else
		{
		  /* Write out all output which was produced.  */
		  if (outbuf > outstart)
		    {
		      const unsigned char *outerr = outstart;
		      int result;

		      result = DL_CALL_FCT (fct, (next_step, next_data,
						  &outerr, outbuf, NULL,
						  irreversible, 0,
						  consume_incomplete));

		      if (result != __GCONV_EMPTY_INPUT)
			{
			  if (__builtin_expect (outerr != outbuf, 0))
			    {
			      /* We have a problem.  Undo the conversion.  */
			      outbuf = outstart;

			      /* Restore the state.  */
# ifdef SAVE_RESET_STATE
			      SAVE_RESET_STATE (0);
# endif
			    }

			  /* Change the status.  */
			  status = result;
			}
		    }

		  if (status == __GCONV_OK)
		    /* Now flush the remaining steps.  */
		    status = DL_CALL_FCT (fct, (next_step, next_data, NULL,
						NULL, NULL, irreversible, 1,
						consume_incomplete));
		}
	    }
	}
      else
#endif
	{
	  /* Clear the state object.  There might be bytes in there from
	     previous calls with CONSUME_INCOMPLETE == 1.  But don't emit
	     escape sequences.  */
	  memset (data->__statep, '\0', sizeof (*data->__statep));

	  if (! (data->__flags & __GCONV_IS_LAST))
	    /* Now flush the remaining steps.  */
	    status = DL_CALL_FCT (fct, (next_step, next_data, NULL, NULL,
					NULL, irreversible, do_flush,
					consume_incomplete));
	}
    }
  else
    {
      /* We preserve the initial values of the pointer variables.  */
      const unsigned char *inptr = *inptrp;
      unsigned char *outbuf = (__builtin_expect (outbufstart == NULL, 1)
			       ? data->__outbuf : *outbufstart);
      unsigned char *outend = data->__outbufend;
      unsigned char *outstart;
      /* This variable is used to count the number of characters we
	 actually converted.  */
      size_t lirreversible = 0;
      size_t *lirreversiblep = irreversible ? &lirreversible : NULL;

      /* The following assumes that encodings, which have a variable length
	 what might unalign a buffer even though it is a aligned in the
	 beginning, either don't have the minimal number of bytes as a divisor
	 of the maximum length or have a minimum length of 1.  This is true
	 for all known and supported encodings.
	 We use && instead of || to combine the subexpression for the FROM
	 encoding and for the TO encoding, because usually one of them is
	 INTERNAL, for which the subexpression evaluates to 1, but INTERNAL
	 buffers are always aligned correctly.  */
#define POSSIBLY_UNALIGNED \
  (!defined _STRING_ARCH_unaligned					      \
   && (((FROM_LOOP_MIN_NEEDED_FROM != 1					      \
	 && FROM_LOOP_MAX_NEEDED_FROM % FROM_LOOP_MIN_NEEDED_FROM == 0)	      \
	&& (FROM_LOOP_MIN_NEEDED_TO != 1				      \
	    && FROM_LOOP_MAX_NEEDED_TO % FROM_LOOP_MIN_NEEDED_TO == 0))	      \
       || ((TO_LOOP_MIN_NEEDED_FROM != 1				      \
	    && TO_LOOP_MAX_NEEDED_FROM % TO_LOOP_MIN_NEEDED_FROM == 0)	      \
	   && (TO_LOOP_MIN_NEEDED_TO != 1				      \
	       && TO_LOOP_MAX_NEEDED_TO % TO_LOOP_MIN_NEEDED_TO == 0))))
#if POSSIBLY_UNALIGNED
      int unaligned;
# define GEN_unaligned(name) GEN_unaligned2 (name)
# define GEN_unaligned2(name) name##_unaligned
#else
# define unaligned 0
#endif

#ifdef PREPARE_LOOP
      PREPARE_LOOP
#endif

#if FROM_LOOP_MAX_NEEDED_FROM > 1 || TO_LOOP_MAX_NEEDED_FROM > 1
      /* If the function is used to implement the mb*towc*() or wc*tomb*()
	 functions we must test whether any bytes from the last call are
	 stored in the `state' object.  */
      if (((FROM_LOOP_MAX_NEEDED_FROM > 1 && TO_LOOP_MAX_NEEDED_FROM > 1)
	   || (FROM_LOOP_MAX_NEEDED_FROM > 1 && FROM_DIRECTION)
	   || (TO_LOOP_MAX_NEEDED_FROM > 1 && !FROM_DIRECTION))
	  && consume_incomplete && (data->__statep->__count & 7) != 0)
	{
	  /* Yep, we have some bytes left over.  Process them now.
	     But this must not happen while we are called from an
	     error handler.  */
	  assert (outbufstart == NULL);

# if FROM_LOOP_MAX_NEEDED_FROM > 1
	  if (TO_LOOP_MAX_NEEDED_FROM == 1 || FROM_DIRECTION)
	    status = SINGLE(FROM_LOOP) (step, data, inptrp, inend, &outbuf,
					outend, lirreversiblep
					EXTRA_LOOP_ARGS);
# endif
# if !ONE_DIRECTION
#  if FROM_LOOP_MAX_NEEDED_FROM > 1 && TO_LOOP_MAX_NEEDED_FROM > 1
	  else
#  endif
#  if TO_LOOP_MAX_NEEDED_FROM > 1
	    status = SINGLE(TO_LOOP) (step, data, inptrp, inend, &outbuf,
				      outend, lirreversiblep EXTRA_LOOP_ARGS);
#  endif
# endif

	  if (__builtin_expect (status, __GCONV_OK) != __GCONV_OK)
	    return status;
	}
#endif

#if POSSIBLY_UNALIGNED
      unaligned =
	((FROM_DIRECTION
	  && ((uintptr_t) inptr % FROM_LOOP_MIN_NEEDED_FROM != 0
	      || ((data->__flags & __GCONV_IS_LAST)
		  && (uintptr_t) outbuf % FROM_LOOP_MIN_NEEDED_TO != 0)))
	 || (!FROM_DIRECTION
	     && (((data->__flags & __GCONV_IS_LAST)
		  && (uintptr_t) outbuf % TO_LOOP_MIN_NEEDED_TO != 0)
		 || (uintptr_t) inptr % TO_LOOP_MIN_NEEDED_FROM != 0)));
#endif

      while (1)
	{
	  struct __gconv_trans_data *trans;

	  /* Remember the start value for this round.  */
	  inptr = *inptrp;
	  /* The outbuf buffer is empty.  */
	  outstart = outbuf;

#ifdef SAVE_RESET_STATE
	  SAVE_RESET_STATE (1);
#endif

	  if (__builtin_expect (!unaligned, 1))
	    {
	      if (FROM_DIRECTION)
		/* Run the conversion loop.  */
		status = FROM_LOOP (step, data, inptrp, inend, &outbuf, outend,
				    lirreversiblep EXTRA_LOOP_ARGS);
	      else
		/* Run the conversion loop.  */
		status = TO_LOOP (step, data, inptrp, inend, &outbuf, outend,
				  lirreversiblep EXTRA_LOOP_ARGS);
	    }
#if POSSIBLY_UNALIGNED
	  else
	    {
	      if (FROM_DIRECTION)
		/* Run the conversion loop.  */
		status = GEN_unaligned (FROM_LOOP) (step, data, inptrp, inend,
						    &outbuf, outend,
						    lirreversiblep
						    EXTRA_LOOP_ARGS);
	      else
		/* Run the conversion loop.  */
		status = GEN_unaligned (TO_LOOP) (step, data, inptrp, inend,
						  &outbuf, outend,
						  lirreversiblep
						  EXTRA_LOOP_ARGS);
	    }
#endif

	  /* If we were called as part of an error handling module we
	     don't do anything else here.  */
	  if (__builtin_expect (outbufstart != NULL, 0))
	    {
	      *outbufstart = outbuf;
	      return status;
	    }

	  /* Give the transliteration module the chance to store the
	     original text and the result in case it needs a context.  */
	  for (trans = data->__trans; trans != NULL; trans = trans->__next)
	    if (trans->__trans_context_fct != NULL)
	      DL_CALL_FCT (trans->__trans_context_fct,
			   (trans->__data, inptr, *inptrp, outstart, outbuf));

	  /* We finished one use of the loops.  */
	  ++data->__invocation_counter;

	  /* If this is the last step leave the loop, there is nothing
	     we can do.  */
	  if (__builtin_expect (data->__flags & __GCONV_IS_LAST, 0))
	    {
	      /* Store information about how many bytes are available.  */
	      data->__outbuf = outbuf;

	      /* Remember how many non-identical characters we
		 converted in a irreversible way.  */
	      *irreversible += lirreversible;

	      break;
	    }

	  /* Write out all output which was produced.  */
	  if (__builtin_expect (outbuf > outstart, 1))
	    {
	      const unsigned char *outerr = data->__outbuf;
	      int result;

	      result = DL_CALL_FCT (fct, (next_step, next_data, &outerr,
					  outbuf, NULL, irreversible, 0,
					  consume_incomplete));

	      if (result != __GCONV_EMPTY_INPUT)
		{
		  if (__builtin_expect (outerr != outbuf, 0))
		    {
#ifdef RESET_INPUT_BUFFER
		      RESET_INPUT_BUFFER;
#else
		      /* We have a problem in one of the functions below.
			 Undo the conversion upto the error point.  */
		      size_t nstatus;

		      /* Reload the pointers.  */
		      *inptrp = inptr;
		      outbuf = outstart;

		      /* Restore the state.  */
# ifdef SAVE_RESET_STATE
		      SAVE_RESET_STATE (0);
# endif

		      if (__builtin_expect (!unaligned, 1))
			{
			  if (FROM_DIRECTION)
			    /* Run the conversion loop.  */
			    nstatus = FROM_LOOP (step, data, inptrp, inend,
						 &outbuf, outerr,
						 lirreversiblep
						 EXTRA_LOOP_ARGS);
			  else
			    /* Run the conversion loop.  */
			    nstatus = TO_LOOP (step, data, inptrp, inend,
					       &outbuf, outerr,
					       lirreversiblep
					       EXTRA_LOOP_ARGS);
			}
# if POSSIBLY_UNALIGNED
		      else
			{
			  if (FROM_DIRECTION)
			    /* Run the conversion loop.  */
			    nstatus = GEN_unaligned (FROM_LOOP) (step, data,
								 inptrp, inend,
								 &outbuf,
								 outerr,
								 lirreversiblep
								 EXTRA_LOOP_ARGS);
			  else
			    /* Run the conversion loop.  */
			    nstatus = GEN_unaligned (TO_LOOP) (step, data,
							       inptrp, inend,
							       &outbuf, outerr,
							       lirreversiblep
							       EXTRA_LOOP_ARGS);
			}
# endif

		      /* We must run out of output buffer space in this
			 rerun.  */
		      assert (outbuf == outerr);
		      assert (nstatus == __GCONV_FULL_OUTPUT);

		      /* If we haven't consumed a single byte decrement
			 the invocation counter.  */
		      if (__builtin_expect (outbuf == outstart, 0))
			--data->__invocation_counter;
#endif	/* reset input buffer */
		    }

		  /* Change the status.  */
		  status = result;
		}
	      else
		/* All the output is consumed, we can make another run
		   if everything was ok.  */
		if (status == __GCONV_FULL_OUTPUT)
		  {
		    status = __GCONV_OK;
		    outbuf = data->__outbuf;
		  }
	    }

	  if (status != __GCONV_OK)
	    break;

	  /* Reset the output buffer pointer for the next round.  */
	  outbuf = data->__outbuf;
	}

#ifdef END_LOOP
      END_LOOP
#endif

      /* If we are supposed to consume all character store now all of the
	 remaining characters in the `state' object.  */
#if FROM_LOOP_MAX_NEEDED_FROM > 1 || TO_LOOP_MAX_NEEDED_FROM > 1
      if (((FROM_LOOP_MAX_NEEDED_FROM > 1 && TO_LOOP_MAX_NEEDED_FROM > 1)
	   || (FROM_LOOP_MAX_NEEDED_FROM > 1 && FROM_DIRECTION)
	   || (TO_LOOP_MAX_NEEDED_FROM > 1 && !FROM_DIRECTION))
	  && __builtin_expect (consume_incomplete, 0)
	  && status == __GCONV_INCOMPLETE_INPUT)
	{
# ifdef STORE_REST
	  mbstate_t *state = data->__statep;

	  STORE_REST
# else
	  /* Make sure the remaining bytes fit into the state objects
	     buffer.  */
	  assert (inend - *inptrp < 4);

	  size_t cnt;
	  for (cnt = 0; *inptrp < inend; ++cnt)
	    data->__statep->__value.__wchb[cnt] = *(*inptrp)++;
	  data->__statep->__count &= ~7;
	  data->__statep->__count |= cnt;
# endif
	}
#endif
#undef unaligned
#undef POSSIBLY_UNALIGNED
    }

  return status;
}
Exemple #15
0
wint_t
__btowc (int c)
{
  const struct gconv_fcts *fcts;

  /* If the parameter does not fit into one byte or it is the EOF value
     we can give the answer now.  */
  if (c < SCHAR_MIN || c > UCHAR_MAX || c == EOF)
    return WEOF;

  /* We know that only ASCII compatible encodings are used for the
     locale and that the wide character encoding is ISO 10646.  */
  if (isascii (c))
    return (wint_t) c;

  /* Get the conversion functions.  */
  fcts = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE));
  __gconv_btowc_fct btowc_fct = fcts->towc->__btowc_fct;
#ifdef PTR_DEMANGLE
  if (fcts->towc->__shlib_handle != NULL)
    PTR_DEMANGLE (btowc_fct);
#endif

  if (__builtin_expect (fcts->towc_nsteps == 1, 1)
      && __builtin_expect (btowc_fct != NULL, 1))
    {
      /* Use the shortcut function.  */
      return DL_CALL_FCT (btowc_fct, (fcts->towc, (unsigned char) c));
    }
  else
    {
      /* Fall back to the slow but generic method.  */
      wchar_t result;
      struct __gconv_step_data data;
      unsigned char inbuf[1];
      const unsigned char *inptr = inbuf;
      size_t dummy;
      int status;

      /* Tell where we want the result.  */
      data.__outbuf = (unsigned char *) &result;
      data.__outbufend = data.__outbuf + sizeof (wchar_t);
      data.__invocation_counter = 0;
      data.__internal_use = 1;
      data.__flags = __GCONV_IS_LAST;
      data.__statep = &data.__state;

      /* Make sure we start in the initial state.  */
      memset (&data.__state, '\0', sizeof (mbstate_t));

      /* Create the input string.  */
      inbuf[0] = c;

      __gconv_fct fct = fcts->towc->__fct;
#ifdef PTR_DEMANGLE
      if (fcts->towc->__shlib_handle != NULL)
	PTR_DEMANGLE (fct);
#endif
      status = DL_CALL_FCT (fct, (fcts->towc, &data, &inptr, inptr + 1,
				  NULL, &dummy, 0, 1));

      if (status != __GCONV_OK && status != __GCONV_FULL_OUTPUT
	  && status != __GCONV_EMPTY_INPUT)
	/* The conversion failed.  */
	result = WEOF;

      return result;
    }
}
Exemple #16
0
size_t
mbrtoc16 (char16_t *pc16, const char *s, size_t n, mbstate_t *ps)
{
  if (ps == NULL)
    ps = &state;

  /* The standard text does not say that S being NULL means the state
     is reset even if the second half of a surrogate still have to be
     returned.  In fact, the error code description indicates
     otherwise.  Therefore always first try to return a second
     half.  */
  if (ps->__count & 0x80000000)
    {
      /* We have to return the second word for a surrogate.  */
      ps->__count &= 0x7fffffff;
      *pc16 = ps->__value.__wch;
      ps->__value.__wch = L'\0';
      return (size_t) -3;
    }

  wchar_t wc;
  struct __gconv_step_data data;
  int status;
  size_t result;
  size_t dummy;
  const unsigned char *inbuf, *endbuf;
  unsigned char *outbuf = (unsigned char *) &wc;
  const struct gconv_fcts *fcts;

  /* Set information for this step.  */
  data.__invocation_counter = 0;
  data.__internal_use = 1;
  data.__flags = __GCONV_IS_LAST;
  data.__statep = ps;
  data.__trans = NULL;

  /* A first special case is if S is NULL.  This means put PS in the
     initial state.  */
  if (s == NULL)
    {
      pc16 = NULL;
      s = "";
      n = 1;
    }

  /* Tell where we want the result.  */
  data.__outbuf = outbuf;
  data.__outbufend = outbuf + sizeof (wchar_t);

  /* Get the conversion functions.  */
  fcts = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE));

  /* Do a normal conversion.  */
  inbuf = (const unsigned char *) s;
  endbuf = inbuf + n;
  if (__glibc_unlikely (endbuf < inbuf))
    {
      endbuf = (const unsigned char *) ~(uintptr_t) 0;
      if (endbuf == inbuf)
	goto ilseq;
    }
  __gconv_fct fct = fcts->towc->__fct;
#ifdef PTR_DEMANGLE
  if (fcts->towc->__shlib_handle != NULL)
    PTR_DEMANGLE (fct);
#endif

  status = DL_CALL_FCT (fct, (fcts->towc, &data, &inbuf, endbuf,
			      NULL, &dummy, 0, 1));

  /* There must not be any problems with the conversion but illegal input
     characters.  The output buffer must be large enough, otherwise the
     definition of MB_CUR_MAX is not correct.  All the other possible
     errors also must not happen.  */
  assert (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT
	  || status == __GCONV_ILLEGAL_INPUT
	  || status == __GCONV_INCOMPLETE_INPUT
	  || status == __GCONV_FULL_OUTPUT);

  if (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT
      || status == __GCONV_FULL_OUTPUT)
    {
      result = inbuf - (const unsigned char *) s;

      if (wc < 0x10000)
	{
	  if (pc16 != NULL)
	    *pc16 = wc;

	  if (data.__outbuf != outbuf && wc == L'\0')
	    {
	      /* The converted character is the NUL character.  */
	      assert (__mbsinit (data.__statep));
	      result = 0;
	    }
	}
      else
	{
	  /* This is a surrogate.  */
	  if (pc16 != NULL)
	    *pc16 = 0xd7c0 + (wc >> 10);

	  ps->__count |= 0x80000000;
	  ps->__value.__wch = 0xdc00 + (wc & 0x3ff);
	}
    }
enum nss_status
check_nonlocal_uid(const char *user, uid_t uid, int *errnop)
{
    enum nss_status status;
    struct passwd pwbuf;
    char *buf;
    size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
    const struct walk_nss w = {
	.lookup = &__nss_passwd_lookup, .fct_name = "getpwuid_r",
	.status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen
    };
    const __typeof__(&_nss_nonlocal_getpwuid_r) self = &_nss_nonlocal_getpwuid_r;
#define args (uid, &pwbuf, buf, buflen, errnop)
#include "walk_nss.h"
#undef args

    if (status == NSS_STATUS_SUCCESS) {
	syslog(LOG_ERR, "nss_nonlocal: possible spoofing attack: non-local user %s has same UID as local user %s!\n", user, pwbuf.pw_name);
	free(buf);
	status = NSS_STATUS_NOTFOUND;
    } else if (status != NSS_STATUS_TRYAGAIN) {
	status = NSS_STATUS_SUCCESS;
    }

    return status;
}

enum nss_status
check_nonlocal_passwd(const char *user, struct passwd *pwd, int *errnop)
{
    enum nss_status status = NSS_STATUS_SUCCESS;
    int old_errno = errno;
    char *end;
    unsigned long uid;

    errno = 0;
    uid = strtoul(pwd->pw_name, &end, 10);
    if (errno == 0 && *end == '\0' && (uid_t)uid == uid) {
	errno = old_errno;
	status = check_nonlocal_uid(user, uid, errnop);
    } else {
	errno = old_errno;
    }
    if (status != NSS_STATUS_SUCCESS)
	return status;

    return check_nonlocal_uid(user, pwd->pw_uid, errnop);
}

enum nss_status
check_nonlocal_user(const char *user, int *errnop)
{
    enum nss_status status;
    struct passwd pwbuf;
    char *buf;
    size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
    const struct walk_nss w = {
	.lookup = __nss_passwd_lookup, .fct_name = "getpwnam_r",
	.status = &status, .errnop = errnop, .buf = &buf, .buflen = &buflen
    };
    const __typeof__(&_nss_nonlocal_getpwnam_r) self = &_nss_nonlocal_getpwnam_r;
#define args (user, &pwbuf, buf, buflen, errnop)
#include "walk_nss.h"
#undef args

    if (status == NSS_STATUS_SUCCESS) {
	free(buf);
	status = NSS_STATUS_NOTFOUND;
    } else if (status != NSS_STATUS_TRYAGAIN) {
	status = NSS_STATUS_SUCCESS;
    }

    return status;
}

enum nss_status
get_nonlocal_passwd(const char *name, struct passwd *pwd, char **buffer,
		    int *errnop)
{
    enum nss_status status;
    size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
    const struct walk_nss w = {
	.lookup = __nss_passwd_nonlocal_lookup, .fct_name = "getpwnam_r",
	.status = &status, .errnop = errnop, .buf = buffer, .buflen = &buflen
    };
    const __typeof__(&_nss_nonlocal_getpwnam_r) self = NULL;
#define args (name, pwd, *buffer, buflen, errnop)
#include "walk_nss.h"
#undef args
    return status;
}


static bool pwent_initialized = false;
static service_user *pwent_startp, *pwent_nip;
static void *pwent_fct_start;
static union {
    enum nss_status (*l)(struct passwd *pwd, char *buffer, size_t buflen,
			 int *errnop);
    void *ptr;
} pwent_fct;
static const char *pwent_fct_name = "getpwent_r";

enum nss_status
_nss_nonlocal_setpwent(int stayopen)
{
    enum nss_status status;
    const struct walk_nss w = {
	.lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "setpwent",
	.status = &status
    };
    const __typeof__(&_nss_nonlocal_setpwent) self = NULL;
#define args (stayopen)
#include "walk_nss.h"
#undef args
    if (status != NSS_STATUS_SUCCESS)
	return status;

    if (!pwent_initialized) {
	__nss_passwd_nonlocal_lookup(&pwent_startp, pwent_fct_name,
				     &pwent_fct_start);
	__sync_synchronize();
	pwent_initialized = true;
    }
    pwent_nip = pwent_startp;
    pwent_fct.ptr = pwent_fct_start;
    return NSS_STATUS_SUCCESS;
}

enum nss_status
_nss_nonlocal_endpwent(void)
{
    enum nss_status status;
    const struct walk_nss w = {
	.lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "endpwent",
	.status = &status, .all_values = 1,
    };
    const __typeof__(&_nss_nonlocal_endpwent) self = NULL;

    pwent_nip = NULL;

#define args ()
#include "walk_nss.h"
#undef args
    return status;
}

enum nss_status
_nss_nonlocal_getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
			 int *errnop)
{
    enum nss_status status;

    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
	return NSS_STATUS_UNAVAIL;

    if (pwent_nip == NULL) {
	status = _nss_nonlocal_setpwent(0);
	if (status != NSS_STATUS_SUCCESS)
	    return status;
    }
    do {
	if (pwent_fct.ptr == NULL)
	    status = NSS_STATUS_UNAVAIL;
	else {
	    int nonlocal_errno;
	    do
		status = DL_CALL_FCT(pwent_fct.l, (pwd, buffer, buflen, errnop));
	    while (status == NSS_STATUS_SUCCESS &&
		   check_nonlocal_passwd(pwd->pw_name, pwd, &nonlocal_errno) != NSS_STATUS_SUCCESS);
	}
	if (status == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
	    return status;

	if (status == NSS_STATUS_SUCCESS)
	    return NSS_STATUS_SUCCESS;
    } while (__nss_next(&pwent_nip, pwent_fct_name, &pwent_fct.ptr, status, 0) == 0);

    pwent_nip = NULL;
    return NSS_STATUS_NOTFOUND;
}


enum nss_status
_nss_nonlocal_getpwnam_r(const char *name, struct passwd *pwd,
			 char *buffer, size_t buflen, int *errnop)
{
    enum nss_status status;
    int group_errno;
    const struct walk_nss w = {
	.lookup = __nss_passwd_nonlocal_lookup, .fct_name = "getpwnam_r",
	.status = &status, .errnop = errnop
    };
    const __typeof__(&_nss_nonlocal_getpwnam_r) self = NULL;

    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
	return NSS_STATUS_UNAVAIL;

#define args (name, pwd, buffer, buflen, errnop)
#include "walk_nss.h"
#undef args
    if (status != NSS_STATUS_SUCCESS)
	return status;

    if (strcmp(name, pwd->pw_name) != 0) {
	syslog(LOG_ERR, "nss_nonlocal: discarding user %s from lookup for user %s\n", pwd->pw_name, name);
	return NSS_STATUS_NOTFOUND;
    }

    status = check_nonlocal_passwd(name, pwd, errnop);
    if (status != NSS_STATUS_SUCCESS)
	return status;

    if (check_nonlocal_gid(name, NULL, pwd->pw_gid, &group_errno) !=
	NSS_STATUS_SUCCESS)
	pwd->pw_gid = 65534 /* nogroup */;
    return NSS_STATUS_SUCCESS;
}

enum nss_status
_nss_nonlocal_getpwuid_r(uid_t uid, struct passwd *pwd,
			 char *buffer, size_t buflen, int *errnop)
{
    enum nss_status status;
    int group_errno;
    const struct walk_nss w = {
	.lookup = &__nss_passwd_nonlocal_lookup, .fct_name = "getpwuid_r",
	.status = &status, .errnop = errnop
    };
    const __typeof__(&_nss_nonlocal_getpwuid_r) self = NULL;

    char *nonlocal_ignore = getenv(NONLOCAL_IGNORE_ENV);
    if (nonlocal_ignore != NULL && nonlocal_ignore[0] != '\0')
	return NSS_STATUS_UNAVAIL;

#define args (uid, pwd, buffer, buflen, errnop)
#include "walk_nss.h"
#undef args
    if (status != NSS_STATUS_SUCCESS)
	return status;

    if (uid != pwd->pw_uid) {
	syslog(LOG_ERR, "nss_nonlocal: discarding uid %d from lookup for uid %d\n", pwd->pw_uid, uid);
	return NSS_STATUS_NOTFOUND;
    }

    status = check_nonlocal_passwd(pwd->pw_name, pwd, errnop);
    if (status != NSS_STATUS_SUCCESS)
	return status;

    if (check_nonlocal_gid(pwd->pw_name, NULL, pwd->pw_gid, &group_errno) !=
	NSS_STATUS_SUCCESS)
	pwd->pw_gid = 65534 /* nogroup */;
    return NSS_STATUS_SUCCESS;
}
Exemple #18
0
int
internal_function
__gconv (__gconv_t cd, const unsigned char **inbuf,
         const unsigned char *inbufend, unsigned char **outbuf,
         unsigned char *outbufend, size_t *irreversible)
{
    size_t last_step;
    int result;

    if (cd == (__gconv_t) -1L)
        return __GCONV_ILLEGAL_DESCRIPTOR;

    last_step = cd->__nsteps - 1;

    assert (irreversible != NULL);
    *irreversible = 0;

    cd->__data[last_step].__outbuf = outbuf != NULL ? *outbuf : NULL;
    cd->__data[last_step].__outbufend = outbufend;

    __gconv_fct fct = cd->__steps->__fct;
#ifdef PTR_DEMANGLE
    if (cd->__steps->__shlib_handle != NULL)
        PTR_DEMANGLE (fct);
#endif

    if (inbuf == NULL || *inbuf == NULL)
    {
        /* We just flush.  */
        result = DL_CALL_FCT (fct,
                              (cd->__steps, cd->__data, NULL, NULL, NULL,
                               irreversible,
                               cd->__data[last_step].__outbuf == NULL ? 2 : 1,
                               0));

        /* If the flush was successful clear the rest of the state.  */
        if (result == __GCONV_OK)
            for (size_t cnt = 0; cnt <= last_step; ++cnt)
                cd->__data[cnt].__invocation_counter = 0;
    }
    else
    {
        const unsigned char *last_start;

        assert (outbuf != NULL && *outbuf != NULL);

        do
        {
            last_start = *inbuf;
            result = DL_CALL_FCT (fct,
                                  (cd->__steps, cd->__data, inbuf, inbufend,
                                   NULL, irreversible, 0, 0));
        }
        while (result == __GCONV_EMPTY_INPUT && last_start != *inbuf
                && *inbuf + cd->__steps->__min_needed_from <= inbufend);
    }

    if (outbuf != NULL && *outbuf != NULL)
        *outbuf = cd->__data[last_step].__outbuf;

    return result;
}
Exemple #19
0
/* Test whether given (host,user,domain) triple is in NETGROUP.  */
int
innetgr (const char *netgroup, const char *host, const char *user,
	 const char *domain)
{
#ifdef USE_NSCD
  if (__nss_not_use_nscd_netgroup > 0
      && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
    __nss_not_use_nscd_netgroup = 0;

  if (!__nss_not_use_nscd_netgroup
      && !__nss_database_custom[NSS_DBSIDX_netgroup])
    {
      int result = __nscd_innetgr (netgroup, host, user, domain);
      if (result >= 0)
	return result;
    }
#endif

  union
  {
    enum nss_status (*f) (const char *, struct __netgrent *);
    void *ptr;
  } setfct;
  void (*endfct) (struct __netgrent *);
  int (*getfct) (struct __netgrent *, char *, size_t, int *);
  struct __netgrent entry;
  int result = 0;
  const char *current_group = netgroup;

  memset (&entry, '\0', sizeof (entry));

  /* Walk through the services until we found an answer or we shall
     not work further.  We can do some optimization here.  Since all
     services must provide the `setnetgrent' function we can do all
     the work during one walk through the service list.  */
  while (1)
    {
      int no_more = setup (&setfct.ptr, &entry.nip);
      while (! no_more)
	{
	  assert (entry.data == NULL);

	  /* Open netgroup.  */
	  enum nss_status status = DL_CALL_FCT (*setfct.f,
						(current_group, &entry));

	  if (status == NSS_STATUS_SUCCESS
	      && (getfct = __nss_lookup_function (entry.nip, "getnetgrent_r"))
		 != NULL)
	    {
	      char buffer[1024];

	      while (DL_CALL_FCT (*getfct,
				  (&entry, buffer, sizeof buffer, &errno))
		     == NSS_STATUS_SUCCESS)
		{
		  if (entry.type == group_val)
		    {
		      /* Make sure we haven't seen the name before.  */
		      struct name_list *namep;

		      for (namep = entry.known_groups; namep != NULL;
			   namep = namep->next)
			if (strcmp (entry.val.group, namep->name) == 0)
			  break;
		      if (namep == NULL)
			for (namep = entry.needed_groups; namep != NULL;
			     namep = namep->next)
			  if (strcmp (entry.val.group, namep->name) == 0)
			    break;
		      if (namep == NULL
			  && strcmp (netgroup, entry.val.group) != 0)
			{
			  size_t group_len = strlen (entry.val.group) + 1;
			  namep =
			    (struct name_list *) malloc (sizeof (*namep)
							 + group_len);
			  if (namep == NULL)
			    {
			      /* Out of memory, simply return.  */
			      result = -1;
			      break;
			    }

			  namep->next = entry.needed_groups;
			  memcpy (namep->name, entry.val.group, group_len);
			  entry.needed_groups = namep;
			}
		    }
		  else
		    {
		      if ((entry.val.triple.host == NULL || host == NULL
			   || __strcasecmp (entry.val.triple.host, host) == 0)
			  && (entry.val.triple.user == NULL || user == NULL
			      || strcmp (entry.val.triple.user, user) == 0)
			  && (entry.val.triple.domain == NULL || domain == NULL
			      || __strcasecmp (entry.val.triple.domain,
					       domain) == 0))
			{
			  result = 1;
			  break;
			}
		    }
		}

	      /* If we found one service which does know the given
		 netgroup we don't try further.  */
	      status = NSS_STATUS_RETURN;
	    }

	  /* Free all resources of the service.  */
	  endfct = __nss_lookup_function (entry.nip, "endnetgrent");
	  if (endfct != NULL)
	    DL_CALL_FCT (*endfct, (&entry));

	  if (result != 0)
	    break;

	  /* Look for the next service.  */
	  no_more = __nss_next2 (&entry.nip, "setnetgrent", NULL,
				 &setfct.ptr, status, 0);
	}

      if (result == 0 && entry.needed_groups != NULL)
	{
	  struct name_list *tmp = entry.needed_groups;
	  entry.needed_groups = tmp->next;
	  tmp->next = entry.known_groups;
	  entry.known_groups = tmp;
	  current_group = tmp->name;
	  continue;
	}

      /* No way out.  */
      break;
    }

  /* Free the memory.  */
  free_memory (&entry);

  return result == 1;
}
Exemple #20
0
size_t
attribute_hidden
__mbsrtowcs_l (wchar_t *dst, const char **src, size_t len, mbstate_t *ps,
	       locale_t l)
{
  struct __gconv_step_data data;
  size_t result;
  int status;
  struct __gconv_step *towc;
  size_t non_reversible;
  const struct gconv_fcts *fcts;

  /* Tell where we want the result.  */
  data.__invocation_counter = 0;
  data.__internal_use = 1;
  data.__flags = __GCONV_IS_LAST;
  data.__statep = ps;

  /* Get the conversion functions.  */
  fcts = get_gconv_fcts (l->__locales[LC_CTYPE]);

  /* Get the structure with the function pointers.  */
  towc = fcts->towc;
  __gconv_fct fct = towc->__fct;
#ifdef PTR_DEMANGLE
  if (towc->__shlib_handle != NULL)
    PTR_DEMANGLE (fct);
#endif

  /* We have to handle DST == NULL special.  */
  if (dst == NULL)
    {
      mbstate_t temp_state;
      wchar_t buf[64];		/* Just an arbitrary size.  */
      const unsigned char *inbuf = (const unsigned char *) *src;
      const unsigned char *srcend = inbuf + strlen (*src) + 1;

      temp_state = *data.__statep;
      data.__statep = &temp_state;

      result = 0;
      data.__outbufend = (unsigned char *) buf + sizeof (buf);
      do
	{
	  data.__outbuf = (unsigned char *) buf;

	  status = DL_CALL_FCT (fct, (towc, &data, &inbuf, srcend, NULL,
				      &non_reversible, 0, 1));

	  result += (wchar_t *) data.__outbuf - buf;
	}
      while (status == __GCONV_FULL_OUTPUT);

      if (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT)
	{
	  /* There better should be a NUL wide char at the end.  */
	  assert (((wchar_t *) data.__outbuf)[-1] == L'\0');
	  /* Don't count the NUL character in.  */
	  --result;
	}
    }
  else
    {
      /* This code is based on the safe assumption that all internal
	 multi-byte encodings use the NUL byte only to mark the end
	 of the string.  */
      const unsigned char *srcp = (const unsigned char *) *src;
      const unsigned char *srcend;

      data.__outbuf = (unsigned char *) dst;
      data.__outbufend = data.__outbuf + len * sizeof (wchar_t);

      status = __GCONV_FULL_OUTPUT;

      while (len > 0)
	{
	  /* Pessimistic guess as to how much input we can use.  In the
	     worst case we need one input byte for one output wchar_t.  */
	  srcend = srcp + __strnlen ((const char *) srcp, len) + 1;

	  status = DL_CALL_FCT (fct, (towc, &data, &srcp, srcend, NULL,
				      &non_reversible, 0, 1));
	  if ((status != __GCONV_EMPTY_INPUT
	       && status != __GCONV_INCOMPLETE_INPUT)
	      /* Not all input read.  */
	      || srcp != srcend
	      /* Reached the end of the input.  */
	      || srcend[-1] == '\0')
	    break;

	  len = (wchar_t *) data.__outbufend - (wchar_t *) data.__outbuf;
	}

      /* Make the end if the input known to the caller.  */
      *src = (const char *) srcp;

      result = (wchar_t *) data.__outbuf - dst;

      /* We have to determine whether the last character converted
	 is the NUL character.  */
      if ((status == __GCONV_OK || status == __GCONV_EMPTY_INPUT)
	  && ((wchar_t *) dst)[result - 1] == L'\0')
	{
	  assert (result > 0);
	  assert (__mbsinit (data.__statep));
	  *src = NULL;
	  --result;
	}
    }

  /* There must not be any problems with the conversion but illegal input
     characters.  */
  assert (status == __GCONV_OK || status == __GCONV_EMPTY_INPUT
	  || status == __GCONV_ILLEGAL_INPUT
	  || status == __GCONV_INCOMPLETE_INPUT
	  || status == __GCONV_FULL_OUTPUT);

  if (status != __GCONV_OK && status != __GCONV_FULL_OUTPUT
      && status != __GCONV_EMPTY_INPUT && status != __GCONV_INCOMPLETE_INPUT)
    {
      result = (size_t) -1;
      __set_errno (EILSEQ);
    }

  return result;
}