1/* -*- buffer-read-only: t -*- vi: set ro: */
2/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3#line 1
4/* Emulate link on platforms that lack it, namely native Windows platforms.
5
6   Copyright (C) 2009, 2010 Free Software Foundation, Inc.
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3, or (at your option)
11   any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software Foundation,
20   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
21
22#include <config.h>
23
24#include <unistd.h>
25
26#include <errno.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/stat.h>
30
31#if !HAVE_LINK
32# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
33
34#  define WIN32_LEAN_AND_MEAN
35#  include <windows.h>
36
37/* CreateHardLink was introduced only in Windows 2000.  */
38typedef BOOL (WINAPI * CreateHardLinkFuncType) (LPCTSTR lpFileName,
39                                                LPCTSTR lpExistingFileName,
40                                                LPSECURITY_ATTRIBUTES lpSecurityAttributes);
41static CreateHardLinkFuncType CreateHardLinkFunc = NULL;
42static BOOL initialized = FALSE;
43
44static void
45initialize (void)
46{
47  HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
48  if (kernel32 != NULL)
49    {
50      CreateHardLinkFunc =
51        (CreateHardLinkFuncType) GetProcAddress (kernel32, "CreateHardLinkA");
52    }
53  initialized = TRUE;
54}
55
56int
57link (const char *file1, const char *file2)
58{
59  char *dir;
60  size_t len1 = strlen (file1);
61  size_t len2 = strlen (file2);
62  if (!initialized)
63    initialize ();
64  if (CreateHardLinkFunc == NULL)
65    {
66      /* System does not support hard links.  */
67      errno = EPERM;
68      return -1;
69    }
70  /* Reject trailing slashes on non-directories; mingw does not
71     support hard-linking directories.  */
72  if ((len1 && (file1[len1 - 1] == '/' || file1[len1 - 1] == '\\'))
73      || (len2 && (file2[len2 - 1] == '/' || file2[len2 - 1] == '\\')))
74    {
75      struct stat st;
76      if (stat (file1, &st) == 0 && S_ISDIR (st.st_mode))
77        errno = EPERM;
78      else
79        errno = ENOTDIR;
80      return -1;
81    }
82  /* CreateHardLink("b/.","a",NULL) creates file "b", so we must check
83     that dirname(file2) exists.  */
84  dir = strdup (file2);
85  if (!dir)
86    return -1;
87  {
88    struct stat st;
89    char *p = strchr (dir, '\0');
90    while (dir < p && (*--p != '/' && *p != '\\'));
91    *p = '\0';
92    if (p != dir && stat (dir, &st) == -1)
93      {
94        int saved_errno = errno;
95        free (dir);
96        errno = saved_errno;
97        return -1;
98      }
99    free (dir);
100  }
101  /* Now create the link.  */
102  if (CreateHardLinkFunc (file2, file1, NULL) == 0)
103    {
104      /* It is not documented which errors CreateHardLink() can produce.
105       * The following conversions are based on tests on a Windows XP SP2
106       * system. */
107      DWORD err = GetLastError ();
108      switch (err)
109        {
110        case ERROR_ACCESS_DENIED:
111          errno = EACCES;
112          break;
113
114        case ERROR_INVALID_FUNCTION:    /* fs does not support hard links */
115          errno = EPERM;
116          break;
117
118        case ERROR_NOT_SAME_DEVICE:
119          errno = EXDEV;
120          break;
121
122        case ERROR_PATH_NOT_FOUND:
123        case ERROR_FILE_NOT_FOUND:
124          errno = ENOENT;
125          break;
126
127        case ERROR_INVALID_PARAMETER:
128          errno = ENAMETOOLONG;
129          break;
130
131        case ERROR_TOO_MANY_LINKS:
132          errno = EMLINK;
133          break;
134
135        case ERROR_ALREADY_EXISTS:
136          errno = EEXIST;
137          break;
138
139        default:
140          errno = EIO;
141        }
142      return -1;
143    }
144
145  return 0;
146}
147
148# else /* !Windows */
149
150#  error "This platform lacks a link function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib."
151
152# endif /* !Windows */
153#else /* HAVE_LINK */
154
155# undef link
156
157/* Create a hard link from FILE1 to FILE2, working around platform bugs.  */
158int
159rpl_link (char const *file1, char const *file2)
160{
161  /* Reject trailing slashes on non-directories.  */
162  size_t len1 = strlen (file1);
163  size_t len2 = strlen (file2);
164  if ((len1 && file1[len1 - 1] == '/')
165      || (len2 && file2[len2 - 1] == '/'))
166    {
167      /* Let link() decide whether hard-linking directories is legal.
168         If stat() fails, then link() should fail for the same reason
169         (although on Solaris 9, link("file/","oops") mistakenly
170         succeeds); if stat() succeeds, require a directory.  */
171      struct stat st;
172      if (stat (file1, &st))
173        return -1;
174      if (!S_ISDIR (st.st_mode))
175        {
176          errno = ENOTDIR;
177          return -1;
178        }
179    }
180  else
181    {
182      /* Fix Cygwin 1.5.x bug where link("a","b/.") creates file "b".  */
183      char *dir = strdup (file2);
184      struct stat st;
185      char *p;
186      if (!dir)
187        return -1;
188      /* We already know file2 does not end in slash.  Strip off the
189         basename, then check that the dirname exists.  */
190      p = strrchr (dir, '/');
191      if (p)
192        {
193          *p = '\0';
194          if (stat (dir, &st) == -1)
195            {
196              int saved_errno = errno;
197              free (dir);
198              errno = saved_errno;
199              return -1;
200            }
201        }
202      free (dir);
203    }
204  return link (file1, file2);
205}
206#endif /* HAVE_LINK */
207