view lib/unlink.c @ 14578:4e83bc0de9e4

Support non-blocking pipe I/O in write() on native Windows. * lib/unistd.in.h (write): Enable replacement also if GNULIB_UNISTD_H_NONBLOCKING is 1. * lib/write.c: Enable replacement also if GNULIB_NONBLOCKING. (rpl_write): When failing to write on a non-blocking pipe, change errno from ENOSPC to EAGAIN. * lib/stdio.in.h (fprintf, fputc, fputs, fwrite, printf, putc, putchar, puts, vfprintf, vprintf): Enable replacement also if GNULIB_STDIO_H_NONBLOCKING is 1. * lib/stdio-write.c: Enable replacements also if GNULIB_NONBLOCKING. (CLEAR_ERRNO, HANDLE_ENOSPC): New macros. (CLEAR_LastError, HANDLE_ERROR_NO_DATA): New macros, extracted from CALL_WITH_SIGPIPE_EMULATION. (CALL_WITH_SIGPIPE_EMULATION): Use them. * m4/nonblocking.m4: New file. * m4/write.m4 (gl_FUNC_WRITE): Enable REPLACE_WRITE also if required for non-blocking I/O support. * m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Initialize GNULIB_UNISTD_H_NONBLOCKING. * m4/stdio_h.m4 (gl_STDIO_H): Enable REPLACE_STDIO_WRITE_FUNCS also if required for non-blocking I/O support. (gl_STDIO_H_DEFAULTS): Initialize GNULIB_STDIO_H_NONBLOCKING. * modules/nonblocking (Files): Add m4/nonblocking.m4, lib/stdio-write.c, m4/asm-underscore.m4. (Depends-on): Add stdio, unistd. (configure.ac): Invoke gl_NONBLOCKING_IO. Define GNULIB_NONBLOCKING. Set GNULIB_STDIO_H_NONBLOCKING, GNULIB_UNISTD_H_NONBLOCKING. * modules/unistd (Makefile.am): Substitute GNULIB_UNISTD_H_NONBLOCKING. * modules/stdio (Makefile.am): Substitute GNULIB_STDIO_H_NONBLOCKING. * doc/posix-functions/fprintf.texi: Mention 'nonblocking' module and problem with non-blocking pipes. * doc/posix-functions/fputc.texi: Likewise. * doc/posix-functions/fputs.texi: Likewise. * doc/posix-functions/fwrite.texi: Likewise. * doc/posix-functions/printf.texi: Likewise. * doc/posix-functions/putc.texi: Likewise. * doc/posix-functions/putchar.texi: Likewise. * doc/posix-functions/puts.texi: Likewise. * doc/posix-functions/vfprintf.texi: Likewise. * doc/posix-functions/vprintf.texi: Likewise. * doc/posix-functions/write.texi: Likewise.
author Bruno Haible <bruno@clisp.org>
date Wed, 13 Apr 2011 12:15:43 +0200
parents 9f47f8c334f2
children 8250f2777afc
line wrap: on
line source

/* Work around unlink bugs.

   Copyright (C) 2009-2011 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>

#include <unistd.h>

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#include "dosname.h"

#undef unlink

/* Remove file NAME.
   Return 0 if successful, -1 if not.  */

int
rpl_unlink (char const *name)
{
  /* Work around Solaris 9 bug where unlink("file/") succeeds.  */
  size_t len = strlen (name);
  int result = 0;
  if (len && ISSLASH (name[len - 1]))
    {
      /* We can't unlink(2) something if it doesn't exist.  If it does
         exist, then it resolved to a directory, due to the trailing
         slash, and POSIX requires that the unlink attempt to remove
         that directory (which would leave the symlink dangling).
         Unfortunately, Solaris 9 is one of the platforms where the
         root user can unlink directories, and we don't want to
         cripple this behavior on real directories, even if it is
         seldom needed (at any rate, it's nicer to let coreutils'
         unlink(1) give the correct errno for non-root users).  But we
         don't know whether name was an actual directory, or a symlink
         to a directory; and due to the bug of ignoring trailing
         slash, Solaris 9 would end up successfully unlinking the
         symlink instead of the directory.  Technically, we could use
         realpath to find the canonical directory name to attempt
         deletion on.  But that is a lot of work for a corner case; so
         we instead just use an lstat on the shortened name, and
         reject symlinks with trailing slashes.  The root user of
         unlink(1) will just have to live with the rule that they
         can't delete a directory via a symlink.  */
      struct stat st;
      result = lstat (name, &st);
      if (result == 0)
        {
          /* Trailing NUL will overwrite the trailing slash.  */
          char *short_name = malloc (len);
          if (!short_name)
            {
              errno = EPERM;
              return -1;
            }
          memcpy (short_name, name, len);
          while (len && ISSLASH (short_name[len - 1]))
            short_name[--len] = '\0';
          if (len && (lstat (short_name, &st) || S_ISLNK (st.st_mode)))
            {
              free (short_name);
              errno = EPERM;
              return -1;
            }
          free (short_name);
        }
    }
  if (!result)
    {
#if UNLINK_PARENT_BUG
      if (len >= 2 && name[len - 1] == '.' && name[len - 2] == '.'
          && (len == 2 || ISSLASH (name[len - 3])))
        {
          errno = EISDIR; /* could also use EPERM */
          return -1;
        }
#endif
      result = unlink (name);
    }
  return result;
}