From 7eff2ff404f2a4961be927de6bd23b7fb702080e Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Mon, 16 May 2022 13:56:05 +0100 Subject: jn: new applet Add a Windows-specific applet to create a directory junction. Usage: jn DIR JUNC where DIR must be an existing directory on a local drive and JUNC must not currently exist. There isn't a simple WIN32 API to create directory junctions. The implementation of mklink in ReactOS provided useful inspiration. --- configs/mingw32_defconfig | 1 + configs/mingw64_defconfig | 1 + include/mingw.h | 1 + miscutils/jn.c | 37 +++++++++++++ win32/mingw.c | 133 +++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 miscutils/jn.c diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig index 65465e81b..78e359500 100644 --- a/configs/mingw32_defconfig +++ b/configs/mingw32_defconfig @@ -816,6 +816,7 @@ CONFIG_FEATURE_CROND_DIR="" # CONFIG_I2CTRANSFER is not set CONFIG_ICONV=y CONFIG_INOTIFYD=y +CONFIG_JN=y CONFIG_LESS=y CONFIG_FEATURE_LESS_MAXLINES=9999999 CONFIG_FEATURE_LESS_BRACKETS=y diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig index bd8750e98..c88da8007 100644 --- a/configs/mingw64_defconfig +++ b/configs/mingw64_defconfig @@ -816,6 +816,7 @@ CONFIG_FEATURE_CROND_DIR="" # CONFIG_I2CTRANSFER is not set CONFIG_ICONV=y CONFIG_INOTIFYD=y +CONFIG_JN=y CONFIG_LESS=y CONFIG_FEATURE_LESS_MAXLINES=9999999 CONFIG_FEATURE_LESS_BRACKETS=y diff --git a/include/mingw.h b/include/mingw.h index 5e01b874e..b91fbb531 100644 --- a/include/mingw.h +++ b/include/mingw.h @@ -479,6 +479,7 @@ NOIMPL(setuid,uid_t gid UNUSED_PARAM); NOIMPL(seteuid,uid_t gid UNUSED_PARAM); unsigned int sleep(unsigned int seconds); int symlink(const char *target, const char *linkpath); +int create_junction(const char *oldpath, const char *newpath); long sysconf(int name); IMPL(getpagesize,int,4096,void); NOIMPL(ttyname_r,int fd UNUSED_PARAM, char *buf UNUSED_PARAM, int sz UNUSED_PARAM); diff --git a/miscutils/jn.c b/miscutils/jn.c new file mode 100644 index 000000000..db6a3e6d9 --- /dev/null +++ b/miscutils/jn.c @@ -0,0 +1,37 @@ +/* + * directory junction creation for busybox + * + * Copyright (C) 2017 Denys Vlasenko + * Copyright (C) 2022 Ron Yorston + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +//config:config JN +//config: bool "jn (3.2 kb)" +//config: default y +//config: depends on PLATFORM_MINGW32 +//config: help +//config: Creates a directory junction. + +//applet:IF_JN(APPLET_NOEXEC(jn, jn, BB_DIR_USR_BIN, BB_SUID_DROP, jn)) + +//kbuild:lib-$(CONFIG_JN) += jn.o + +//usage:#define jn_trivial_usage +//usage: "DIR JUNC" +//usage:#define jn_full_usage "\n\n" +//usage: "Create directory junction JUNC to DIR" + +#include "libbb.h" + +int jn_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int jn_main(int argc UNUSED_PARAM, char **argv) +{ + getopt32(argv, "^" "" "\0" "=2"); + argv += optind; + if (create_junction(argv[0], argv[1]) != 0) { + bb_perror_msg_and_die("can't create junction '%s' to '%s'", + argv[1], argv[0]); + } + return EXIT_SUCCESS; +} diff --git a/win32/mingw.c b/win32/mingw.c index c35b1d6af..79aaa86f1 100644 --- a/win32/mingw.c +++ b/win32/mingw.c @@ -1285,6 +1285,138 @@ int symlink(const char *target, const char *linkpath) return 0; } +/* Create a directory junction */ +#define MRPB rptr->MountPointReparseBuffer +#if 0 +static void print_junction(REPARSE_DATA_BUFFER *rptr) +{ + int i; +#define MRPB_HEADER_SIZE \ + (FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - \ + FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer)) + + fprintf(stderr, "---\n"); + fprintf(stderr, "Tag: %lx\n", rptr->ReparseTag); + fprintf(stderr, "ReparseDataLength: %d (%d + %d + %d + %d + %d = %d)\n", + rptr->ReparseDataLength, MRPB_HEADER_SIZE, + MRPB.SubstituteNameLength, sizeof(WCHAR), + MRPB.PrintNameLength, sizeof(WCHAR), + MRPB_HEADER_SIZE + MRPB.SubstituteNameLength + sizeof(WCHAR) + + MRPB.PrintNameLength + sizeof(WCHAR)); + fprintf(stderr, "Reserved: %d\n", rptr->Reserved); + fprintf(stderr, "---\n"); + fprintf(stderr, "SubstituteNameOffset: %d\n", MRPB.SubstituteNameOffset); + fprintf(stderr, "SubstituteNameLength: %d\n", MRPB.SubstituteNameLength); + fprintf(stderr, "PrintNameOffset: %d\n", MRPB.PrintNameOffset); + fprintf(stderr, "PrintNameLength: %d\n", MRPB.PrintNameLength); + fprintf(stderr, "SubstituteName: "); + for (i = 0; i < MRPB.SubstituteNameLength/sizeof(WCHAR); i++) + fprintf(stderr, "%c", + MRPB.PathBuffer[MRPB.SubstituteNameOffset/sizeof(WCHAR) + i]); + fprintf(stderr, " (%x)", + MRPB.PathBuffer[MRPB.SubstituteNameOffset/sizeof(WCHAR) + i]); + fprintf(stderr, "\n"); + fprintf(stderr, "PrintName: "); + for (i = 0; i < MRPB.PrintNameLength/sizeof(WCHAR); i++) + fprintf(stderr, "%c", + MRPB.PathBuffer[MRPB.PrintNameOffset/sizeof(WCHAR) + i]); + fprintf(stderr, " (%x)", + MRPB.PathBuffer[MRPB.PrintNameOffset/sizeof(WCHAR) + i]); + fprintf(stderr, "\n"); + fprintf(stderr, "---\n"); +} +#endif + +static REPARSE_DATA_BUFFER *make_junction_data_buffer(char *rpath) +{ + WCHAR pbuf[PATH_MAX]; + int plen, slen, rbufsize; + REPARSE_DATA_BUFFER *rptr; + + /* We need two strings for the reparse data. The PrintName is the + * target path in Win32 format, the SubstituteName is the same in + * NT format. */ + slash_to_bs(rpath); + plen = MultiByteToWideChar(CP_ACP, 0, rpath, -1, pbuf, PATH_MAX); + if (plen == 0) { + errno = err_win_to_posix(); + return NULL; + } + slen = plen + 4; + + rbufsize = (slen + plen + 2) * sizeof(WCHAR) + + FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer); + rptr = xzalloc(rbufsize); + + rptr->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + rptr->ReparseDataLength = rbufsize - + FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer); + /* rptr->Reserved = 0; */ + /* MRPB.SubstituteNameOffset = 0; */ + MRPB.SubstituteNameLength = slen * sizeof(WCHAR); + MRPB.PrintNameOffset = MRPB.SubstituteNameLength + sizeof(WCHAR); + MRPB.PrintNameLength = plen * sizeof(WCHAR); + + wcscpy(MRPB.PathBuffer, L"\\??\\"); + wcscpy(MRPB.PathBuffer + 4, pbuf); + wcscpy(MRPB.PathBuffer + slen + 1, pbuf); + return rptr; +} + +int create_junction(const char *oldpath, const char *newpath) +{ + char rpath[PATH_MAX]; + struct stat statbuf; + REPARSE_DATA_BUFFER *rptr = NULL; + HANDLE h; + int error = 0; + DWORD bytes; + + if (realpath(oldpath, rpath) == NULL || stat(rpath, &statbuf) < 0) + return -1; + + if (!has_dos_drive_prefix(rpath)) { + errno = EINVAL; + return -1; + } + + if (!S_ISDIR(statbuf.st_mode)) { + errno = ENOTDIR; + return -1; + } + + if (!(rptr = make_junction_data_buffer(rpath))) { + return -1; + } + + if (mkdir(newpath, 0777) < 0) { + free(rptr); + return -1; + } + + h = CreateFileA(newpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (h != INVALID_HANDLE_VALUE) { + if (DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, rptr, + rptr->ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE, + NULL, 0, &bytes, NULL) != 0) { + CloseHandle(h); + free(rptr); + return 0; + } + error = err_win_to_posix(); + CloseHandle(h); + } else { + error = err_win_to_posix(); + } + + rmdir(newpath); + free(rptr); + errno = error; + return -1; +} + static char *normalize_ntpathA(char *buf) { /* fix absolute path prefixes */ @@ -1399,7 +1531,6 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf) } #define SRPB rptr->SymbolicLinkReparseBuffer -#define MRPB rptr->MountPointReparseBuffer ssize_t readlink(const char *pathname, char *buf, size_t bufsiz) { HANDLE h; -- cgit v1.2.3-55-g6feb