view lib/link.c @ 16214:ec738d6aeef5

Talk about "native Windows API", not "Win32". * lib/classpath.c: Update comments to mention native Windows. * lib/csharpexec.c: Likewise. * lib/dup2.c: Likewise. * lib/error.c: Likewise. * lib/fcntl.c: Likewise. * lib/filename.h: Likewise. * lib/findprog.c: Likewise. * lib/get-rusage-as.c: Likewise. * lib/get-rusage-data.c: Likewise. * lib/getpagesize.c: Likewise. * lib/javaexec.c: Likewise. * lib/msvc-inval.c: Likewise. * lib/msvc-nothrow.c: Likewise. * lib/nanosleep.c: Likewise. * lib/nonblocking.c: Likewise. * lib/printf-parse.c: Likewise. * lib/setlocale.c: Likewise. * lib/sigaction.c: Likewise. * lib/strerror_r.c: Likewise. * lib/tmpdir.c: Likewise. * lib/vasnprintf.c: Likewise. * lib/w32spawn.h: Likewise. * lib/waitpid.c: Likewise. * lib/stdio.in.h (fdopen, fopen, freopen): Likewise. * m4/locale-ar.m4: Likewise. * m4/locale-fr.m4: Likewise. * m4/locale-ja.m4: Likewise. * m4/locale-tr.m4: Likewise. * m4/locale-zh.m4: Likewise. * m4/printf.m4: Likewise. * tests/test-cloexec.c: Likewise. * tests/test-copy-acl.sh: Likewise. * tests/test-copy-file.sh: Likewise. * tests/test-file-has-acl.sh: Likewise. * tests/test-set-mode-acl.sh: Likewise. * tests/test-dup-safer.c: Likewise. * tests/test-dup2.c: Likewise. * tests/test-dup3.c: Likewise. * tests/test-fcntl.c: Likewise. * tests/test-nonblocking-pipe.h: Likewise. * tests/test-nonblocking-socket.h: Likewise. * tests/test-pipe.c: Likewise. * tests/test-pipe2.c: Likewise. * tests/test-spawn-pipe-child.c: Likewise. * doc/acl-resources.txt: Likewise. * lib/getaddrinfo.c (WINDOWS_NATIVE): Renamed from WIN32_NATIVE. * tests/test-poll.c (WINDOWS_NATIVE): Likewise. * tests/test-select.h (WINDOWS_NATIVE): Likewise. * lib/localcharset.c: Update comments to mention native Windows. (WINDOWS_NATIVE): Renamed from WIN32_NATIVE. * lib/localename.c: Likewise. * lib/progreloc.c: Likewise. * lib/relocatable.c: Likewise. * lib/poll.c (WINDOWS_NATIVE): Renamed from WIN32_NATIVE. (windows_compute_revents): Renamed from win32_compute_revents. (windows_compute_revents_socket): Renamed from win32_compute_revents_socket. * lib/select.c: Update comments to mention native Windows. (windows_poll_handle): Renamed from win32_poll_handle. * m4/threadlib.m4: Update comments to mention native Windows. (gl_THREADLIB_EARLY_BODY, gl_THREADLIB_BODY): Expect --enable-threads=windows instead of --enable-threads=win32. Set USE_WINDOWS_THREADS, not USE_WIN32_THREADS. * lib/glthread/lock.h: Update comments to mention native Windows. (USE_WINDOWS_THREADS): Renamed from USE_WIN32_THREADS. * lib/glthread/lock.c (USE_WINDOWS_THREADS): Renamed from USE_WIN32_THREADS. * lib/glthread/cond.h (USE_WINDOWS_THREADS): Likewise. * lib/glthread/cond.c (USE_WINDOWS_THREADS): Likewise. * lib/glthread/thread.h (USE_WINDOWS_THREADS): Likewise. * lib/glthread/thread.c (USE_WINDOWS_THREADS): Likewise. * lib/glthread/tls.h (USE_WINDOWS_THREADS): Likewise. * lib/glthread/tls.c (USE_WINDOWS_THREADS): Likewise. * lib/glthread/yield.h (USE_WINDOWS_THREADS): Likewise. * tests/test-cond.c (USE_WINDOWS_THREADS): Likewise. * tests/test-thread_create.c (USE_WINDOWS_THREADS): Likewise. * tests/test-lock.c (USE_WINDOWS_THREADS): Likewise. (TEST_WINDOWS_THREADS): Renamed from TEST_WIN32_THREADS. * tests/test-tls.c: Likewise. Rationale: Microsoft renamed the "Win32 API" to "Windows API", as it is available on both 32-bit and 64-bit Windows systems. But in gnulib, we treat Cygwin like a Unix platform, therefore the main line of distinction is between "native Windows" on one side and Unix/ POSIX systems on the other side. More details in <https://lists.gnu.org/archive/html/bug-gnulib/2012-01/msg00027.html>. Suggested by Paul Eggert.
author Bruno Haible <bruno@clisp.org>
date Wed, 04 Jan 2012 14:31:33 +0100
parents 8250f2777afc
children bb182ee4a09d
line wrap: on
line source

/* Emulate link on platforms that lack it, namely native Windows platforms.

   Copyright (C) 2009-2012 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 2, 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, write to the Free Software Foundation,
   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */

#include <config.h>

#include <unistd.h>

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

#if !HAVE_LINK
# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__

#  define WIN32_LEAN_AND_MEAN
#  include <windows.h>

/* CreateHardLink was introduced only in Windows 2000.  */
typedef BOOL (WINAPI * CreateHardLinkFuncType) (LPCTSTR lpFileName,
                                                LPCTSTR lpExistingFileName,
                                                LPSECURITY_ATTRIBUTES lpSecurityAttributes);
static CreateHardLinkFuncType CreateHardLinkFunc = NULL;
static BOOL initialized = FALSE;

static void
initialize (void)
{
  HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
  if (kernel32 != NULL)
    {
      CreateHardLinkFunc =
        (CreateHardLinkFuncType) GetProcAddress (kernel32, "CreateHardLinkA");
    }
  initialized = TRUE;
}

int
link (const char *file1, const char *file2)
{
  char *dir;
  size_t len1 = strlen (file1);
  size_t len2 = strlen (file2);
  if (!initialized)
    initialize ();
  if (CreateHardLinkFunc == NULL)
    {
      /* System does not support hard links.  */
      errno = EPERM;
      return -1;
    }
  /* Reject trailing slashes on non-directories; mingw does not
     support hard-linking directories.  */
  if ((len1 && (file1[len1 - 1] == '/' || file1[len1 - 1] == '\\'))
      || (len2 && (file2[len2 - 1] == '/' || file2[len2 - 1] == '\\')))
    {
      struct stat st;
      if (stat (file1, &st) == 0 && S_ISDIR (st.st_mode))
        errno = EPERM;
      else
        errno = ENOTDIR;
      return -1;
    }
  /* CreateHardLink("b/.","a",NULL) creates file "b", so we must check
     that dirname(file2) exists.  */
  dir = strdup (file2);
  if (!dir)
    return -1;
  {
    struct stat st;
    char *p = strchr (dir, '\0');
    while (dir < p && (*--p != '/' && *p != '\\'));
    *p = '\0';
    if (p != dir && stat (dir, &st) == -1)
      {
        int saved_errno = errno;
        free (dir);
        errno = saved_errno;
        return -1;
      }
    free (dir);
  }
  /* Now create the link.  */
  if (CreateHardLinkFunc (file2, file1, NULL) == 0)
    {
      /* It is not documented which errors CreateHardLink() can produce.
       * The following conversions are based on tests on a Windows XP SP2
       * system. */
      DWORD err = GetLastError ();
      switch (err)
        {
        case ERROR_ACCESS_DENIED:
          errno = EACCES;
          break;

        case ERROR_INVALID_FUNCTION:    /* fs does not support hard links */
          errno = EPERM;
          break;

        case ERROR_NOT_SAME_DEVICE:
          errno = EXDEV;
          break;

        case ERROR_PATH_NOT_FOUND:
        case ERROR_FILE_NOT_FOUND:
          errno = ENOENT;
          break;

        case ERROR_INVALID_PARAMETER:
          errno = ENAMETOOLONG;
          break;

        case ERROR_TOO_MANY_LINKS:
          errno = EMLINK;
          break;

        case ERROR_ALREADY_EXISTS:
          errno = EEXIST;
          break;

        default:
          errno = EIO;
        }
      return -1;
    }

  return 0;
}

# else /* !Windows */

#  error "This platform lacks a link function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib."

# endif /* !Windows */
#else /* HAVE_LINK */

# undef link

/* Create a hard link from FILE1 to FILE2, working around platform bugs.  */
int
rpl_link (char const *file1, char const *file2)
{
  size_t len1;
  size_t len2;
  struct stat st;

  /* Don't allow IRIX to dereference dangling file2 symlink.  */
  if (!lstat (file2, &st))
    {
      errno = EEXIST;
      return -1;
    }

  /* Reject trailing slashes on non-directories.  */
  len1 = strlen (file1);
  len2 = strlen (file2);
  if ((len1 && file1[len1 - 1] == '/')
      || (len2 && file2[len2 - 1] == '/'))
    {
      /* Let link() decide whether hard-linking directories is legal.
         If stat() fails, then link() should fail for the same reason
         (although on Solaris 9, link("file/","oops") mistakenly
         succeeds); if stat() succeeds, require a directory.  */
      if (stat (file1, &st))
        return -1;
      if (!S_ISDIR (st.st_mode))
        {
          errno = ENOTDIR;
          return -1;
        }
    }
  else
    {
      /* Fix Cygwin 1.5.x bug where link("a","b/.") creates file "b".  */
      char *dir = strdup (file2);
      char *p;
      if (!dir)
        return -1;
      /* We already know file2 does not end in slash.  Strip off the
         basename, then check that the dirname exists.  */
      p = strrchr (dir, '/');
      if (p)
        {
          *p = '\0';
          if (stat (dir, &st) == -1)
            {
              int saved_errno = errno;
              free (dir);
              errno = saved_errno;
              return -1;
            }
        }
      free (dir);
    }
  return link (file1, file2);
}
#endif /* HAVE_LINK */