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; |
