aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2022-05-16 13:56:05 +0100
committerRon Yorston <rmy@pobox.com>2022-05-16 14:03:51 +0100
commit7eff2ff404f2a4961be927de6bd23b7fb702080e (patch)
tree04d0e0252e904733a723bb536e9aef3504f0325f
parent7fb95a2a569ca6d68dad4cef5b7b299e9fe68e2b (diff)
downloadbusybox-w32-7eff2ff404f2a4961be927de6bd23b7fb702080e.tar.gz
busybox-w32-7eff2ff404f2a4961be927de6bd23b7fb702080e.tar.bz2
busybox-w32-7eff2ff404f2a4961be927de6bd23b7fb702080e.zip
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.
-rw-r--r--configs/mingw32_defconfig1
-rw-r--r--configs/mingw64_defconfig1
-rw-r--r--include/mingw.h1
-rw-r--r--miscutils/jn.c37
-rw-r--r--win32/mingw.c133
5 files changed, 172 insertions, 1 deletions
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=""
816# CONFIG_I2CTRANSFER is not set 816# CONFIG_I2CTRANSFER is not set
817CONFIG_ICONV=y 817CONFIG_ICONV=y
818CONFIG_INOTIFYD=y 818CONFIG_INOTIFYD=y
819CONFIG_JN=y
819CONFIG_LESS=y 820CONFIG_LESS=y
820CONFIG_FEATURE_LESS_MAXLINES=9999999 821CONFIG_FEATURE_LESS_MAXLINES=9999999
821CONFIG_FEATURE_LESS_BRACKETS=y 822CONFIG_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=""
816# CONFIG_I2CTRANSFER is not set 816# CONFIG_I2CTRANSFER is not set
817CONFIG_ICONV=y 817CONFIG_ICONV=y
818CONFIG_INOTIFYD=y 818CONFIG_INOTIFYD=y
819CONFIG_JN=y
819CONFIG_LESS=y 820CONFIG_LESS=y
820CONFIG_FEATURE_LESS_MAXLINES=9999999 821CONFIG_FEATURE_LESS_MAXLINES=9999999
821CONFIG_FEATURE_LESS_BRACKETS=y 822CONFIG_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);
479NOIMPL(seteuid,uid_t gid UNUSED_PARAM); 479NOIMPL(seteuid,uid_t gid UNUSED_PARAM);
480unsigned int sleep(unsigned int seconds); 480unsigned int sleep(unsigned int seconds);
481int symlink(const char *target, const char *linkpath); 481int symlink(const char *target, const char *linkpath);
482int create_junction(const char *oldpath, const char *newpath);
482long sysconf(int name); 483long sysconf(int name);
483IMPL(getpagesize,int,4096,void); 484IMPL(getpagesize,int,4096,void);
484NOIMPL(ttyname_r,int fd UNUSED_PARAM, char *buf UNUSED_PARAM, int sz UNUSED_PARAM); 485NOIMPL(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 @@
1/*
2 * directory junction creation for busybox
3 *
4 * Copyright (C) 2017 Denys Vlasenko <vda.linux@googlemail.com>
5 * Copyright (C) 2022 Ron Yorston <rmy@pobox.com>
6 *
7 * Licensed under GPLv2, see file LICENSE in this source tree.
8 */
9//config:config JN
10//config: bool "jn (3.2 kb)"
11//config: default y
12//config: depends on PLATFORM_MINGW32
13//config: help
14//config: Creates a directory junction.
15
16//applet:IF_JN(APPLET_NOEXEC(jn, jn, BB_DIR_USR_BIN, BB_SUID_DROP, jn))
17
18//kbuild:lib-$(CONFIG_JN) += jn.o
19
20//usage:#define jn_trivial_usage
21//usage: "DIR JUNC"
22//usage:#define jn_full_usage "\n\n"
23//usage: "Create directory junction JUNC to DIR"
24
25#include "libbb.h"
26
27int jn_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
28int jn_main(int argc UNUSED_PARAM, char **argv)
29{
30 getopt32(argv, "^" "" "\0" "=2");
31 argv += optind;
32 if (create_junction(argv[0], argv[1]) != 0) {
33 bb_perror_msg_and_die("can't create junction '%s' to '%s'",
34 argv[1], argv[0]);
35 }
36 return EXIT_SUCCESS;
37}
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)
1285 return 0; 1285 return 0;
1286} 1286}
1287 1287
1288/* Create a directory junction */
1289#define MRPB rptr->MountPointReparseBuffer
1290#if 0
1291static void print_junction(REPARSE_DATA_BUFFER *rptr)
1292{
1293 int i;
1294#define MRPB_HEADER_SIZE \
1295 (FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - \
1296 FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer))
1297
1298 fprintf(stderr, "---\n");
1299 fprintf(stderr, "Tag: %lx\n", rptr->ReparseTag);
1300 fprintf(stderr, "ReparseDataLength: %d (%d + %d + %d + %d + %d = %d)\n",
1301 rptr->ReparseDataLength, MRPB_HEADER_SIZE,
1302 MRPB.SubstituteNameLength, sizeof(WCHAR),
1303 MRPB.PrintNameLength, sizeof(WCHAR),
1304 MRPB_HEADER_SIZE + MRPB.SubstituteNameLength + sizeof(WCHAR) +
1305 MRPB.PrintNameLength + sizeof(WCHAR));
1306 fprintf(stderr, "Reserved: %d\n", rptr->Reserved);
1307 fprintf(stderr, "---\n");
1308 fprintf(stderr, "SubstituteNameOffset: %d\n", MRPB.SubstituteNameOffset);
1309 fprintf(stderr, "SubstituteNameLength: %d\n", MRPB.SubstituteNameLength);
1310 fprintf(stderr, "PrintNameOffset: %d\n", MRPB.PrintNameOffset);
1311 fprintf(stderr, "PrintNameLength: %d\n", MRPB.PrintNameLength);
1312 fprintf(stderr, "SubstituteName: ");
1313 for (i = 0; i < MRPB.SubstituteNameLength/sizeof(WCHAR); i++)
1314 fprintf(stderr, "%c",
1315 MRPB.PathBuffer[MRPB.SubstituteNameOffset/sizeof(WCHAR) + i]);
1316 fprintf(stderr, " (%x)",
1317 MRPB.PathBuffer[MRPB.SubstituteNameOffset/sizeof(WCHAR) + i]);
1318 fprintf(stderr, "\n");
1319 fprintf(stderr, "PrintName: ");
1320 for (i = 0; i < MRPB.PrintNameLength/sizeof(WCHAR); i++)
1321 fprintf(stderr, "%c",
1322 MRPB.PathBuffer[MRPB.PrintNameOffset/sizeof(WCHAR) + i]);
1323 fprintf(stderr, " (%x)",
1324 MRPB.PathBuffer[MRPB.PrintNameOffset/sizeof(WCHAR) + i]);
1325 fprintf(stderr, "\n");
1326 fprintf(stderr, "---\n");
1327}
1328#endif
1329
1330static REPARSE_DATA_BUFFER *make_junction_data_buffer(char *rpath)
1331{
1332 WCHAR pbuf[PATH_MAX];
1333 int plen, slen, rbufsize;
1334 REPARSE_DATA_BUFFER *rptr;
1335
1336 /* We need two strings for the reparse data. The PrintName is the
1337 * target path in Win32 format, the SubstituteName is the same in
1338 * NT format. */
1339 slash_to_bs(rpath);
1340 plen = MultiByteToWideChar(CP_ACP, 0, rpath, -1, pbuf, PATH_MAX);
1341 if (plen == 0) {
1342 errno = err_win_to_posix();
1343 return NULL;
1344 }
1345 slen = plen + 4;
1346
1347 rbufsize = (slen + plen + 2) * sizeof(WCHAR) +
1348 FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer);
1349 rptr = xzalloc(rbufsize);
1350
1351 rptr->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
1352 rptr->ReparseDataLength = rbufsize -
1353 FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer);
1354 /* rptr->Reserved = 0; */
1355 /* MRPB.SubstituteNameOffset = 0; */
1356 MRPB.SubstituteNameLength = slen * sizeof(WCHAR);
1357 MRPB.PrintNameOffset = MRPB.SubstituteNameLength + sizeof(WCHAR);
1358 MRPB.PrintNameLength = plen * sizeof(WCHAR);
1359
1360 wcscpy(MRPB.PathBuffer, L"\\??\\");
1361 wcscpy(MRPB.PathBuffer + 4, pbuf);
1362 wcscpy(MRPB.PathBuffer + slen + 1, pbuf);
1363 return rptr;
1364}
1365
1366int create_junction(const char *oldpath, const char *newpath)
1367{
1368 char rpath[PATH_MAX];
1369 struct stat statbuf;
1370 REPARSE_DATA_BUFFER *rptr = NULL;
1371 HANDLE h;
1372 int error = 0;
1373 DWORD bytes;
1374
1375 if (realpath(oldpath, rpath) == NULL || stat(rpath, &statbuf) < 0)
1376 return -1;
1377
1378 if (!has_dos_drive_prefix(rpath)) {
1379 errno = EINVAL;
1380 return -1;
1381 }
1382
1383 if (!S_ISDIR(statbuf.st_mode)) {
1384 errno = ENOTDIR;
1385 return -1;
1386 }
1387
1388 if (!(rptr = make_junction_data_buffer(rpath))) {
1389 return -1;
1390 }
1391
1392 if (mkdir(newpath, 0777) < 0) {
1393 free(rptr);
1394 return -1;
1395 }
1396
1397 h = CreateFileA(newpath, GENERIC_READ | GENERIC_WRITE, 0, NULL,
1398 OPEN_EXISTING,
1399 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
1400 if (h != INVALID_HANDLE_VALUE) {
1401 if (DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, rptr,
1402 rptr->ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE,
1403 NULL, 0, &bytes, NULL) != 0) {
1404 CloseHandle(h);
1405 free(rptr);
1406 return 0;
1407 }
1408 error = err_win_to_posix();
1409 CloseHandle(h);
1410 } else {
1411 error = err_win_to_posix();
1412 }
1413
1414 rmdir(newpath);
1415 free(rptr);
1416 errno = error;
1417 return -1;
1418}
1419
1288static char *normalize_ntpathA(char *buf) 1420static char *normalize_ntpathA(char *buf)
1289{ 1421{
1290 /* fix absolute path prefixes */ 1422 /* fix absolute path prefixes */
@@ -1399,7 +1531,6 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
1399} 1531}
1400 1532
1401#define SRPB rptr->SymbolicLinkReparseBuffer 1533#define SRPB rptr->SymbolicLinkReparseBuffer
1402#define MRPB rptr->MountPointReparseBuffer
1403ssize_t readlink(const char *pathname, char *buf, size_t bufsiz) 1534ssize_t readlink(const char *pathname, char *buf, size_t bufsiz)
1404{ 1535{
1405 HANDLE h; 1536 HANDLE h;