1
0
mirror of https://https.git.savannah.gnu.org/git/gnulib.git synced 2026-04-28 06:33:36 +00:00
Files
gnulib/lib/link.c
Alejandro Colomar 68a407f668 Use strnul in a few places.
* lib/argz.c (argz_next): Use strnul.
* lib/cpu-supports.c (hwcap_allowed): Likewise.
* lib/file-has-acl.c (aclinfo_has_xattr): Likewise.
* lib/inet_ntop.c (inet_ntop6): Likewise.
* lib/link.c (link): Likewise.
* lib/localename-unsafe.c (enum_locales_fn): Likewise.
* lib/mbspcasecmp.c (mbspcasecmp): Likewise.
* lib/opendir.c (opendir): Likewise.
* lib/parse-duration.c (parse_year_month_day, parse_hour_minute_second,
trim): Likewise.
* lib/setlocale.c (setlocale_unixlike): Likewise.
* lib/strftime.c (__strftime_internal): Likewise.
* lib/striconv.c (str_cd_iconv): Likewise.
* lib/strncat.c (strncat): Likewise.
* lib/term-style-control.c (log_signal_handler_called,
tcsetattr_failed): Likewise.
* lib/time_rz.c (save_abbr): Likewise.
* lib/vc-mtime.c (git_mtime, max_vc_mtime): Likewise.
* tests/test-savedir.c (test_savedir_sort_none, test_savedir_sort_name):
Likewise.
* modules/argz (Depends-on): Add strnul.
* modules/cpu-supports (Depends-on): Likewise.
* modules/file-has-acl (Depends-on): Likewise.
* modules/inet_ntop (Depends-on): Likewise.
* modules/link (Depends-on): Likewise.
* modules/localename-unsafe (Depends-on): Likewise.
* modules/localename-unsafe-limited (Depends-on): Likewise.
* modules/mbspcasecmp (Depends-on): Likewise.
* modules/opendir (Depends-on): Likewise.
* modules/parse-duration (Depends-on): Likewise.
* modules/setlocale (Depends-on): Likewise.
* modules/nstrftime (Depends-on): Likewise.
* modules/nstrftime-limited (Depends-on): Likewise.
* modules/c-nstrftime (Depends-on): Likewise.
* modules/fprintftime (Depends-on): Likewise.
* modules/striconv (Depends-on): Likewise.
* modules/strncat (Depends-on): Likewise.
* modules/term-style-control (Depends-on): Likewise.
* modules/time_rz (Depends-on): Likewise.
* modules/vc-mtime (Depends-on): Likewise.
* modules/savedir-tests (Depends-on): Likewise.

Copyright-paperwork-exempt: Yes
2026-02-24 02:59:14 +01:00

236 lines
6.2 KiB
C

/* Emulate link on platforms that lack it, namely native Windows platforms.
Copyright (C) 2009-2026 Free Software Foundation, Inc.
This file is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This file 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
#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 __CYGWIN__
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
/* Don't assume that UNICODE is not defined. */
# undef GetModuleHandle
# define GetModuleHandle GetModuleHandleA
# undef CreateHardLink
# define CreateHardLink CreateHardLinkA
# if !(_WIN32_WINNT >= _WIN32_WINNT_WINXP)
/* Avoid warnings from gcc -Wcast-function-type. */
# define GetProcAddress \
(void *) GetProcAddress
/* CreateHardLink was introduced only in Windows 2000. */
typedef BOOL (WINAPI * CreateHardLinkFuncType) (LPCSTR lpFileName,
LPCSTR 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;
}
# else
# define CreateHardLinkFunc CreateHardLink
# endif
int
link (const char *file1, const char *file2)
{
# if !(_WIN32_WINNT >= _WIN32_WINNT_WINXP)
if (!initialized)
initialize ();
# endif
if (CreateHardLinkFunc == NULL)
{
/* System does not support hard links. */
errno = EPERM;
return -1;
}
/* Reject trailing slashes on non-directories; native Windows does not
support hard-linking directories. */
{
size_t len1 = strlen (file1);
size_t len2 = strlen (file2);
if ((len1 && (file1[len1 - 1] == '/' || file1[len1 - 1] == '\\'))
|| (len2 && (file2[len2 - 1] == '/' || file2[len2 - 1] == '\\')))
{
/* If stat() fails, then link() should fail for the same reason. */
struct stat st;
if (stat (file1, &st))
{
if (errno == EOVERFLOW)
/* It's surely a file, not a directory (see stat-w32.c). */
errno = ENOTDIR;
return -1;
}
if (!S_ISDIR (st.st_mode))
errno = ENOTDIR;
else
errno = EPERM;
return -1;
}
}
/* CreateHardLink("b/.","a",NULL) creates file "b", so we must check
that dirname(file2) exists. */
char *dir = strdup (file2);
if (!dir)
return -1;
{
struct stat st;
char *p = strnul (dir);
while (dir < p && (*--p != '/' && *p != '\\'));
*p = '\0';
if (p != dir && stat (dir, &st) != 0 && errno != EOVERFLOW)
{
free (dir);
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)
{
struct stat st;
/* Don't allow IRIX to dereference dangling file2 symlink. */
if (lstat (file2, &st) == 0 || errno == EOVERFLOW)
{
errno = EEXIST;
return -1;
}
/* Reject trailing slashes on non-directories. */
size_t len1 = strlen (file1);
size_t 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);
if (!dir)
return -1;
/* We already know file2 does not end in slash. Strip off the
basename, then check that the dirname exists. */
char *p = strrchr (dir, '/');
if (p)
{
*p = '\0';
if (stat (dir, &st) != 0 && errno != EOVERFLOW)
{
free (dir);
return -1;
}
}
free (dir);
}
return link (file1, file2);
}
#endif /* HAVE_LINK */