diff options
author | Ron Yorston <rmy@pobox.com> | 2022-05-16 13:56:05 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2022-05-16 14:03:51 +0100 |
commit | 7eff2ff404f2a4961be927de6bd23b7fb702080e (patch) | |
tree | 04d0e0252e904733a723bb536e9aef3504f0325f | |
parent | 7fb95a2a569ca6d68dad4cef5b7b299e9fe68e2b (diff) | |
download | busybox-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_defconfig | 1 | ||||
-rw-r--r-- | configs/mingw64_defconfig | 1 | ||||
-rw-r--r-- | include/mingw.h | 1 | ||||
-rw-r--r-- | miscutils/jn.c | 37 | ||||
-rw-r--r-- | win32/mingw.c | 133 |
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 |
817 | CONFIG_ICONV=y | 817 | CONFIG_ICONV=y |
818 | CONFIG_INOTIFYD=y | 818 | CONFIG_INOTIFYD=y |
819 | CONFIG_JN=y | ||
819 | CONFIG_LESS=y | 820 | CONFIG_LESS=y |
820 | CONFIG_FEATURE_LESS_MAXLINES=9999999 | 821 | CONFIG_FEATURE_LESS_MAXLINES=9999999 |
821 | CONFIG_FEATURE_LESS_BRACKETS=y | 822 | 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="" | |||
816 | # CONFIG_I2CTRANSFER is not set | 816 | # CONFIG_I2CTRANSFER is not set |
817 | CONFIG_ICONV=y | 817 | CONFIG_ICONV=y |
818 | CONFIG_INOTIFYD=y | 818 | CONFIG_INOTIFYD=y |
819 | CONFIG_JN=y | ||
819 | CONFIG_LESS=y | 820 | CONFIG_LESS=y |
820 | CONFIG_FEATURE_LESS_MAXLINES=9999999 | 821 | CONFIG_FEATURE_LESS_MAXLINES=9999999 |
821 | CONFIG_FEATURE_LESS_BRACKETS=y | 822 | 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); | |||
479 | NOIMPL(seteuid,uid_t gid UNUSED_PARAM); | 479 | NOIMPL(seteuid,uid_t gid UNUSED_PARAM); |
480 | unsigned int sleep(unsigned int seconds); | 480 | unsigned int sleep(unsigned int seconds); |
481 | int symlink(const char *target, const char *linkpath); | 481 | int symlink(const char *target, const char *linkpath); |
482 | int create_junction(const char *oldpath, const char *newpath); | ||
482 | long sysconf(int name); | 483 | long sysconf(int name); |
483 | IMPL(getpagesize,int,4096,void); | 484 | IMPL(getpagesize,int,4096,void); |
484 | NOIMPL(ttyname_r,int fd UNUSED_PARAM, char *buf UNUSED_PARAM, int sz UNUSED_PARAM); | 485 | 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 @@ | |||
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 | |||
27 | int jn_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
28 | int 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 | ||
1291 | static 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 | |||
1330 | static 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 | |||
1366 | int 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 | |||
1288 | static char *normalize_ntpathA(char *buf) | 1420 | static 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 | ||
1403 | ssize_t readlink(const char *pathname, char *buf, size_t bufsiz) | 1534 | ssize_t readlink(const char *pathname, char *buf, size_t bufsiz) |
1404 | { | 1535 | { |
1405 | HANDLE h; | 1536 | HANDLE h; |