From 7587b56c10a4d11fe434e3eaa51212113f09ec10 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Fri, 12 Feb 2021 14:11:01 +0000 Subject: win32: implement symlink(2) Provide an implementation of symlink(2). Calls to symlink(2) will fail in default Windows installations unless running with elevated privileges. Failure to create a symlink when extracting files from an archive is therefore treated as a non-fatal error. There are two ways to permit the creation of symlinks: - Edit security policy to give users the 'Create symbolic links' privilege. Unfortunately this doesn't work for users who are an Administrator. - Enable developer mode, which is available in later versions of Windows 10. The ability to create symlinks is not available in Windows XP or ReactOS. --- archival/libarchive/unsafe_symlink_target.c | 30 ++++++++++++++++--------- include/mingw.h | 2 +- win32/mingw.c | 35 +++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/archival/libarchive/unsafe_symlink_target.c b/archival/libarchive/unsafe_symlink_target.c index b5f5a2670..8e4cd4380 100644 --- a/archival/libarchive/unsafe_symlink_target.c +++ b/archival/libarchive/unsafe_symlink_target.c @@ -5,12 +5,17 @@ #include "libbb.h" #include "bb_archive.h" +/* symlink may not be available for WIN32, just issue a warning */ +#if ENABLE_PLATFORM_MINGW32 +# undef bb_perror_msg_and_die +# define bb_perror_msg_and_die(...) bb_perror_msg(__VA_ARGS__) +#endif + void FAST_FUNC create_or_remember_link(llist_t **link_placeholders, const char *target, const char *linkname, int hard_link) { -#if !ENABLE_PLATFORM_MINGW32 if (hard_link || target[0] == '/' || strstr(target, "..")) { llist_add_to_end(link_placeholders, xasprintf("%c%s%c%s", hard_link, linkname, '\0', target) @@ -23,16 +28,6 @@ void FAST_FUNC create_or_remember_link(llist_t **link_placeholders, "sym", linkname, target ); } -#else - if (hard_link) { - llist_add_to_end(link_placeholders, - xasprintf("%c%s%c%s", hard_link, linkname, '\0', target) - ); - return; - } - /* symlink isn't implemented for WIN32, just issue a warning */ - bb_perror_msg("can't create symlink '%s' to '%s'", linkname, target); -#endif } void FAST_FUNC create_links_from_list(llist_t *list) @@ -42,11 +37,24 @@ void FAST_FUNC create_links_from_list(llist_t *list) target = list->data + 1 + strlen(list->data + 1) + 1; if ((*list->data ? link : symlink) (target, list->data + 1)) { +#if !ENABLE_PLATFORM_MINGW32 /* shared message */ bb_error_msg_and_die("can't create %slink '%s' to '%s'", *list->data ? "hard" : "sym", list->data + 1, target ); +#else + if (!*list->data) + bb_error_msg("can't create %slink '%s' to '%s'", + "sym", + list->data + 1, target + ); + else + bb_error_msg_and_die("can't create %slink '%s' to '%s'", + "hard", + list->data + 1, target + ); +#endif } list = list->link; } diff --git a/include/mingw.h b/include/mingw.h index 713205ef9..e2c02babf 100644 --- a/include/mingw.h +++ b/include/mingw.h @@ -440,7 +440,7 @@ NOIMPL(setsid,void); NOIMPL(setuid,uid_t gid UNUSED_PARAM); NOIMPL(seteuid,uid_t gid UNUSED_PARAM); unsigned int sleep(unsigned int seconds); -NOIMPL(symlink,const char *oldpath UNUSED_PARAM, const char *newpath UNUSED_PARAM); +int symlink(const char *target, const char *linkpath); static inline void sync(void) {} long sysconf(int name); IMPL(getpagesize,int,4096,void); diff --git a/win32/mingw.c b/win32/mingw.c index 41dba9857..a6362333d 100644 --- a/win32/mingw.c +++ b/win32/mingw.c @@ -1063,6 +1063,41 @@ int link(const char *oldpath, const char *newpath) return 0; } +#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY +# define SYMBOLIC_LINK_FLAG_DIRECTORY (0x1) +#endif +#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE +# define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2) +#endif + +int symlink(const char *target, const char *linkpath) +{ + DWORD flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + struct stat st; + DECLARE_PROC_ADDR(BOOL, CreateSymbolicLinkA, LPCSTR, LPCSTR, DWORD); + + if (!INIT_PROC_ADDR(kernel32.dll, CreateSymbolicLinkA)) { + return -1; + } + + if (stat(target, &st) != -1 && S_ISDIR(st.st_mode)) + flag |= SYMBOLIC_LINK_FLAG_DIRECTORY; + + retry: + if (!CreateSymbolicLinkA(linkpath, target, flag)) { + /* Old Windows versions see 'UNPRIVILEGED_CREATE' as an invalid + * parameter. Retry without it. */ + if ((flag & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) && + GetLastError() == ERROR_INVALID_PARAMETER) { + flag &= ~SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + goto retry; + } + errno = err_win_to_posix(); + return -1; + } + return 0; +} + static char *normalize_ntpathA(char *buf) { /* fix absolute path prefixes */ -- cgit v1.2.3-55-g6feb