view lib/fchdir.c @ 11224:5aa57cee93aa

avoid gcc 3.4.3 bug on long double NaN on Irix 6.5 * tests/nan.h (NaNl): Rewrite as function on Irix, to avoid compilation bug by using runtime conversion. * m4/isfinite.m4 (gl_ISFINITE): Likewise. * m4/isnanl.m4 (gl_FUNC_ISNANL): Likewise. * modules/ceill-tests (Files): Use nan.h. * modules/floorl-tests (Files): Likewise. * modules/frexpl-tests (Files): Likewise. * modules/isnanl-tests (Files): Likewise. * modules/ldexpl-tests (Files): Likewise. * modules/roundl-tests (Files): Likewise. * modules/truncl-tests (Files): Likewise. * tests/test-ceill.c (main): Use a working NaN. * tests/test-floorl.c (main): Likewise. * tests/test-frexpl.c (main): Likewise. * tests/test-isnan.c (test_long_double): Likewise. * tests/test-isnanl.h (main): Likewise. * tests/test-ldexpl.h (main): Likewise. * tests/test-roundl.h (main): Likewise. * tests/test-truncl.h (main): Likewise. See http://lists.gnu.org/archive/html/bug-gnulib/2009-02/msg00190.html. Signed-off-by: Eric Blake <ebb9@byu.net>
author Eric Blake <ebb9@byu.net>
date Thu, 26 Feb 2009 20:18:42 -0700
parents 2b0380d48ff6
children c694fa3f9d7d
line wrap: on
line source

/* fchdir replacement.
   Copyright (C) 2006-2008 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include <config.h>

/* Specification.  */
#include <unistd.h>

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "canonicalize.h"

/* This replacement assumes that a directory is not renamed while opened
   through a file descriptor.  */

/* Array of file descriptors opened.  If it points to a directory, it stores
   info about this directory; otherwise it stores an errno value of ENOTDIR.  */
typedef struct
{
  char *name;       /* Absolute name of the directory, or NULL.  */
  int saved_errno;  /* If name == NULL: The error code describing the failure
		       reason.  */
} dir_info_t;
static dir_info_t *dirs;
static size_t dirs_allocated;

/* Try to ensure dirs has enough room for a slot at index fd.  */
static void
ensure_dirs_slot (size_t fd)
{
  if (fd >= dirs_allocated)
    {
      size_t new_allocated;
      dir_info_t *new_dirs;
      size_t i;

      new_allocated = 2 * dirs_allocated + 1;
      if (new_allocated <= fd)
	new_allocated = fd + 1;
      new_dirs =
	(dirs != NULL
	 ? (dir_info_t *) realloc (dirs, new_allocated * sizeof (dir_info_t))
	 : (dir_info_t *) malloc (new_allocated * sizeof (dir_info_t)));
      if (new_dirs != NULL)
	{
	  for (i = dirs_allocated; i < new_allocated; i++)
	    {
	      new_dirs[i].name = NULL;
	      new_dirs[i].saved_errno = ENOTDIR;
	    }
	  dirs = new_dirs;
	  dirs_allocated = new_allocated;
	}
    }
}

/* Hook into the gnulib replacements for open() and close() to keep track
   of the open file descriptors.  */

void
_gl_unregister_fd (int fd)
{
  if (fd >= 0 && fd < dirs_allocated)
    {
      if (dirs[fd].name != NULL)
	free (dirs[fd].name);
      dirs[fd].name = NULL;
      dirs[fd].saved_errno = ENOTDIR;
    }
}

void
_gl_register_fd (int fd, const char *filename)
{
  struct stat statbuf;

  ensure_dirs_slot (fd);
  if (fd < dirs_allocated
      && fstat (fd, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
    {
      dirs[fd].name = canonicalize_file_name (filename);
      if (dirs[fd].name == NULL)
	dirs[fd].saved_errno = errno;
    }
}

/* Override opendir() and closedir(), to keep track of the open file
   descriptors.  Needed because there is a function dirfd().  */

int
rpl_closedir (DIR *dp)
#undef closedir
{
  int fd = dirfd (dp);
  int retval = closedir (dp);

  if (retval >= 0)
    _gl_unregister_fd (fd);
  return retval;
}

DIR *
rpl_opendir (const char *filename)
#undef opendir
{
  DIR *dp;

  dp = opendir (filename);
  if (dp != NULL)
    {
      int fd = dirfd (dp);
      if (fd >= 0)
	_gl_register_fd (fd, filename);
    }
  return dp;
}

/* Override dup() and dup2(), to keep track of open file descriptors.  */

int
rpl_dup (int oldfd)
#undef dup
{
  int newfd = dup (oldfd);

  if (oldfd >= 0 && newfd >= 0)
    {
      ensure_dirs_slot (newfd);
      if (newfd < dirs_allocated)
	{
	  if (oldfd < dirs_allocated)
	    {
	      if (dirs[oldfd].name != NULL)
		{
		  dirs[newfd].name = strdup (dirs[oldfd].name);
		  if (dirs[newfd].name == NULL)
		    dirs[newfd].saved_errno = ENOMEM;
		}
	      else
		{
		  dirs[newfd].name = NULL;
		  dirs[newfd].saved_errno = dirs[oldfd].saved_errno;
		}
	    }
	  else
	    {
	      dirs[newfd].name = NULL;
	      dirs[newfd].saved_errno = ENOMEM;
	    }
	}
    }
  return newfd;
}

int
rpl_dup2 (int oldfd, int newfd)
#undef dup2
{
  int retval = dup2 (oldfd, newfd);

  if (retval >= 0 && oldfd >= 0 && newfd >= 0 && newfd != oldfd)
    {
      ensure_dirs_slot (newfd);
      if (newfd < dirs_allocated)
	{
	  if (oldfd < dirs_allocated)
	    {
	      if (dirs[oldfd].name != NULL)
		{
		  dirs[newfd].name = strdup (dirs[oldfd].name);
		  if (dirs[newfd].name == NULL)
		    dirs[newfd].saved_errno = ENOMEM;
		}
	      else
		{
		  dirs[newfd].name = NULL;
		  dirs[newfd].saved_errno = dirs[oldfd].saved_errno;
		}
	    }
	  else
	    {
	      dirs[newfd].name = NULL;
	      dirs[newfd].saved_errno = ENOMEM;
	    }
	}
    }
  return retval;
}

/* Implement fchdir() in terms of chdir().  */

int
fchdir (int fd)
{
  if (fd >= 0)
    {
      if (fd < dirs_allocated)
	{
	  if (dirs[fd].name != NULL)
	    return chdir (dirs[fd].name);
	  else
	    {
	      errno = dirs[fd].saved_errno;
	      return -1;
	    }
	}
      else
	{
	  errno = ENOMEM;
	  return -1;
	}
    }
  else
    {
      errno = EBADF;
      return -1;
    }
}