diff options
author | Ron Yorston <rmy@pobox.com> | 2024-07-13 08:29:09 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2024-07-13 08:29:09 +0100 |
commit | b18891bba511d4fc4fcd0a6ff5cd2df31a086f1b (patch) | |
tree | ef78f9ecc339d6ab95eed03f787f058f270b8772 | |
parent | 684dabdb8452b3d33d5d6265f3d7ef32c10f5307 (diff) | |
parent | 23da5c4b716b92524240c6f81c2e2474c1825cfc (diff) | |
download | busybox-w32-b18891bba511d4fc4fcd0a6ff5cd2df31a086f1b.tar.gz busybox-w32-b18891bba511d4fc4fcd0a6ff5cd2df31a086f1b.tar.bz2 busybox-w32-b18891bba511d4fc4fcd0a6ff5cd2df31a086f1b.zip |
Merge branch 'busybox' into merge
-rw-r--r-- | coreutils/md5_sha1_sum.c | 4 | ||||
-rw-r--r-- | editors/vi.c | 7 | ||||
-rw-r--r-- | libbb/hash_md5_sha.c | 29 | ||||
-rw-r--r-- | networking/tls_sp_c32.c | 91 | ||||
-rw-r--r-- | shell/ash.c | 40 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir3.right | 2 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir_exec2.right | 4 | ||||
-rwxr-xr-x | shell/ash_test/ash-redir/redir_exec2.tests | 10 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir_to_bad_fd255.right | 2 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir_to_bad_fd3.right | 2 | ||||
-rw-r--r-- | shell/hush.c | 75 | ||||
-rw-r--r-- | shell/hush_test/hush-redir/redir3.right | 3 | ||||
-rw-r--r-- | shell/hush_test/hush-redir/redir_exec2.right | 4 | ||||
-rwxr-xr-x | shell/hush_test/hush-redir/redir_exec2.tests | 10 | ||||
-rw-r--r-- | shell/hush_test/hush-redir/redir_to_bad_fd.right | 3 | ||||
-rw-r--r-- | shell/hush_test/hush-redir/redir_to_bad_fd255.right | 3 | ||||
-rw-r--r-- | shell/hush_test/hush-redir/redir_to_bad_fd3.right | 3 |
17 files changed, 208 insertions, 84 deletions
diff --git a/coreutils/md5_sha1_sum.c b/coreutils/md5_sha1_sum.c index 15f2c04de..978d328f1 100644 --- a/coreutils/md5_sha1_sum.c +++ b/coreutils/md5_sha1_sum.c | |||
@@ -320,11 +320,7 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv) | |||
320 | 320 | ||
321 | hash_value = hash_file(in_buf, filename_ptr, sha3_width); | 321 | hash_value = hash_file(in_buf, filename_ptr, sha3_width); |
322 | 322 | ||
323 | #if ENABLE_PLATFORM_MINGW32 | ||
324 | if (hash_value && (strcasecmp((char*)hash_value, line) == 0)) { | 323 | if (hash_value && (strcasecmp((char*)hash_value, line) == 0)) { |
325 | #else | ||
326 | if (hash_value && (strcmp((char*)hash_value, line) == 0)) { | ||
327 | #endif | ||
328 | if (!(flags & FLAG_SILENT)) | 324 | if (!(flags & FLAG_SILENT)) |
329 | printf("%s: OK\n", filename_ptr); | 325 | printf("%s: OK\n", filename_ptr); |
330 | } else { | 326 | } else { |
diff --git a/editors/vi.c b/editors/vi.c index 7ad8412a2..e59083ddb 100644 --- a/editors/vi.c +++ b/editors/vi.c | |||
@@ -2422,9 +2422,10 @@ static int init_text_buffer(char *fn) | |||
2422 | 2422 | ||
2423 | update_filename(fn); | 2423 | update_filename(fn); |
2424 | rc = file_insert(fn, text, 1); | 2424 | rc = file_insert(fn, text, 1); |
2425 | if (rc < 0) { | 2425 | if (rc <= 0 || *(end - 1) != '\n') { |
2426 | // file doesnt exist. Start empty buf with dummy line | 2426 | // file doesn't exist or doesn't end in a newline. |
2427 | char_insert(text, '\n', NO_UNDO); | 2427 | // insert a newline to the end |
2428 | char_insert(end, '\n', NO_UNDO); | ||
2428 | } | 2429 | } |
2429 | 2430 | ||
2430 | flush_undo_data(); | 2431 | flush_undo_data(); |
diff --git a/libbb/hash_md5_sha.c b/libbb/hash_md5_sha.c index 88baf51dc..57a801459 100644 --- a/libbb/hash_md5_sha.c +++ b/libbb/hash_md5_sha.c | |||
@@ -15,18 +15,28 @@ | |||
15 | 15 | ||
16 | #if ENABLE_SHA1_HWACCEL || ENABLE_SHA256_HWACCEL | 16 | #if ENABLE_SHA1_HWACCEL || ENABLE_SHA256_HWACCEL |
17 | # if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) | 17 | # if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) |
18 | static void cpuid(unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx) | 18 | static void cpuid_eax_ebx_ecx(unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx) |
19 | { | 19 | { |
20 | asm ("cpuid" | 20 | asm ("cpuid" |
21 | : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) | 21 | : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) |
22 | : "0"(*eax), "1"(*ebx), "2"(*ecx), "3"(*edx) | 22 | : "0" (*eax), "1" (*ebx), "2" (*ecx) |
23 | ); | 23 | ); |
24 | } | 24 | } |
25 | static smallint shaNI; | 25 | static smallint shaNI; |
26 | static int get_shaNI(void) | 26 | static NOINLINE int get_shaNI(void) |
27 | { | 27 | { |
28 | unsigned eax = 7, ebx = ebx, ecx = 0, edx = edx; | 28 | /* Get leaf 7 subleaf 0. Exists on all CPUs since Merom (2006). |
29 | cpuid(&eax, &ebx, &ecx, &edx); | 29 | * "If a value entered for CPUID.EAX is higher than the maximum |
30 | * input value for basic or extended function for that processor | ||
31 | * then the data for the highest basic information leaf is returned". | ||
32 | * This means that Pentiums 4 would return leaf 5 or 6 instead of 7, | ||
33 | * which happen to have zero in EBX bit 29. Thus they should work too. | ||
34 | */ | ||
35 | unsigned eax = 7; | ||
36 | unsigned ecx = 0; | ||
37 | unsigned ebx = 0; /* should not be needed, paranoia */ | ||
38 | unsigned edx; | ||
39 | cpuid_eax_ebx_ecx(&eax, &ebx, &ecx, &edx); | ||
30 | ebx = ((ebx >> 28) & 2) - 1; /* bit 29 -> 1 or -1 */ | 40 | ebx = ((ebx >> 28) & 2) - 1; /* bit 29 -> 1 or -1 */ |
31 | shaNI = (int)ebx; | 41 | shaNI = (int)ebx; |
32 | return (int)ebx; | 42 | return (int)ebx; |
@@ -1300,7 +1310,14 @@ unsigned FAST_FUNC sha1_end(sha1_ctx_t *ctx, void *resbuf) | |||
1300 | /* SHA stores total in BE, need to swap on LE arches: */ | 1310 | /* SHA stores total in BE, need to swap on LE arches: */ |
1301 | common64_end(ctx, /*swap_needed:*/ BB_LITTLE_ENDIAN); | 1311 | common64_end(ctx, /*swap_needed:*/ BB_LITTLE_ENDIAN); |
1302 | 1312 | ||
1303 | hash_size = (ctx->process_block == sha1_process_block64) ? 5 : 8; | 1313 | hash_size = 8; |
1314 | if (ctx->process_block == sha1_process_block64 | ||
1315 | #if ENABLE_SHA1_HWACCEL | ||
1316 | || ctx->process_block == sha1_process_block64_shaNI | ||
1317 | #endif | ||
1318 | ) { | ||
1319 | hash_size = 5; | ||
1320 | } | ||
1304 | /* This way we do not impose alignment constraints on resbuf: */ | 1321 | /* This way we do not impose alignment constraints on resbuf: */ |
1305 | if (BB_LITTLE_ENDIAN) { | 1322 | if (BB_LITTLE_ENDIAN) { |
1306 | unsigned i; | 1323 | unsigned i; |
diff --git a/networking/tls_sp_c32.c b/networking/tls_sp_c32.c index 999033034..e493c436a 100644 --- a/networking/tls_sp_c32.c +++ b/networking/tls_sp_c32.c | |||
@@ -411,10 +411,10 @@ static void sp_256_sub_8_p256_mod(sp_digit* r) | |||
411 | "\n subl $0xffffffff, (%0)" | 411 | "\n subl $0xffffffff, (%0)" |
412 | "\n sbbl $0xffffffff, 1*4(%0)" | 412 | "\n sbbl $0xffffffff, 1*4(%0)" |
413 | "\n sbbl $0xffffffff, 2*4(%0)" | 413 | "\n sbbl $0xffffffff, 2*4(%0)" |
414 | "\n sbbl $0, 3*4(%0)" | 414 | "\n sbbl $0x00000000, 3*4(%0)" |
415 | "\n sbbl $0, 4*4(%0)" | 415 | "\n sbbl $0x00000000, 4*4(%0)" |
416 | "\n sbbl $0, 5*4(%0)" | 416 | "\n sbbl $0x00000000, 5*4(%0)" |
417 | "\n sbbl $1, 6*4(%0)" | 417 | "\n sbbl $0x00000001, 6*4(%0)" |
418 | "\n sbbl $0xffffffff, 7*4(%0)" | 418 | "\n sbbl $0xffffffff, 7*4(%0)" |
419 | "\n" | 419 | "\n" |
420 | : "=r" (r) | 420 | : "=r" (r) |
@@ -422,29 +422,48 @@ static void sp_256_sub_8_p256_mod(sp_digit* r) | |||
422 | : "memory" | 422 | : "memory" |
423 | ); | 423 | ); |
424 | } | 424 | } |
425 | #elif ALLOW_ASM && defined(__GNUC__) && defined(__x86_64__) && ENABLE_PLATFORM_POSIX | 425 | #elif ALLOW_ASM && defined(__GNUC__) && defined(__x86_64__) |
426 | static void sp_256_sub_8_p256_mod(sp_digit* r) | 426 | static void sp_256_sub_8_p256_mod(sp_digit* r) |
427 | { | 427 | { |
428 | //p256_mod[3..0] = ffffffff00000001 0000000000000000 00000000ffffffff ffffffffffffffff | ||
429 | # if 0 | ||
430 | // gcc -Oz bug (?) https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115875 | ||
431 | // uses buggy "push $-1; pop %rax" insns to load 00000000ffffffff | ||
428 | uint64_t reg; | 432 | uint64_t reg; |
429 | uint64_t ooff; | 433 | uint64_t ooff; |
430 | //p256_mod[3..0] = ffffffff00000001 0000000000000000 00000000ffffffff ffffffffffffffff | ||
431 | asm volatile ( | 434 | asm volatile ( |
432 | "\n addq $1, (%0)" // adding 1 is the same as subtracting ffffffffffffffff | 435 | "\n subq $0xffffffffffffffff, (%0)" |
433 | "\n cmc" // only carry bit needs inverting | 436 | "\n sbbq %1, 1*8(%0)" // %1 = 00000000ffffffff |
434 | "\n" | 437 | "\n sbbq $0x0000000000000000, 2*8(%0)" |
435 | "\n sbbq %1, 1*8(%0)" // %1 holds 00000000ffffffff | ||
436 | "\n" | ||
437 | "\n sbbq $0, 2*8(%0)" | ||
438 | "\n" | ||
439 | "\n movq 3*8(%0), %2" | 438 | "\n movq 3*8(%0), %2" |
440 | "\n sbbq $0, %2" // adding 00000000ffffffff (in %1) | 439 | "\n sbbq $0x0, %2" // subtract carry |
441 | "\n addq %1, %2" // is the same as subtracting ffffffff00000001 | 440 | "\n addq %1, %2" // adding 00000000ffffffff (in %1) |
441 | "\n" // is the same as subtracting ffffffff00000001 | ||
442 | "\n movq %2, 3*8(%0)" | 442 | "\n movq %2, 3*8(%0)" |
443 | "\n" | 443 | "\n" |
444 | : "=r" (r), "=r" (ooff), "=r" (reg) | 444 | : "=r" (r), "=r" (ooff), "=r" (reg) |
445 | : "0" (r), "1" (0x00000000ffffffff) | 445 | : "0" (r), "1" (0x00000000ffffffffUL) /* UL is important! */ |
446 | : "memory" | ||
447 | ); | ||
448 | # else // let's do it by hand: | ||
449 | uint64_t reg; | ||
450 | uint64_t rax; | ||
451 | asm volatile ( | ||
452 | "\n orl $0xffffffff, %%eax" // %1 (rax) = 00000000ffffffff | ||
453 | "\n subq $0xffffffffffffffff, (%0)" | ||
454 | "\n sbbq %1, 1*8(%0)" | ||
455 | "\n sbbq $0x0000000000000000, 2*8(%0)" | ||
456 | "\n movq 3*8(%0), %2" | ||
457 | "\n sbbq $0x0, %2" // subtract carry | ||
458 | "\n addq %1, %2" // adding 00000000ffffffff (in %1) | ||
459 | "\n" // is the same as subtracting ffffffff00000001 | ||
460 | "\n movq %2, 3*8(%0)" | ||
461 | "\n" | ||
462 | : "=r" (r), "=&a" (rax), "=r" (reg) | ||
463 | : "0" (r) | ||
446 | : "memory" | 464 | : "memory" |
447 | ); | 465 | ); |
466 | # endif | ||
448 | } | 467 | } |
449 | #else | 468 | #else |
450 | static void sp_256_sub_8_p256_mod(sp_digit* r) | 469 | static void sp_256_sub_8_p256_mod(sp_digit* r) |
@@ -476,15 +495,23 @@ static void sp_256to512_mul_8(sp_digit* r, const sp_digit* a, const sp_digit* b) | |||
476 | //////////////////////// | 495 | //////////////////////// |
477 | // uint64_t m = ((uint64_t)a[i]) * b[j]; | 496 | // uint64_t m = ((uint64_t)a[i]) * b[j]; |
478 | // acc_hi:acch:accl += m; | 497 | // acc_hi:acch:accl += m; |
498 | long eax_clobbered; | ||
479 | asm volatile ( | 499 | asm volatile ( |
480 | // a[i] is already loaded in %%eax | 500 | // a[i] is already loaded in %%eax |
481 | "\n mull %7" | 501 | "\n mull %8" |
482 | "\n addl %%eax, %0" | 502 | "\n addl %%eax, %0" |
483 | "\n adcl %%edx, %1" | 503 | "\n adcl %%edx, %1" |
484 | "\n adcl $0, %2" | 504 | "\n adcl $0x0, %2" |
485 | : "=rm" (accl), "=rm" (acch), "=rm" (acc_hi) | 505 | : "=rm" (accl), "=rm" (acch), "=rm" (acc_hi), "=a" (eax_clobbered) |
486 | : "0" (accl), "1" (acch), "2" (acc_hi), "a" (a[i]), "m" (b[j]) | 506 | : "0" (accl), "1" (acch), "2" (acc_hi), "3" (a[i]), "m" (b[j]) |
487 | : "cc", "dx" | 507 | : "cc", "dx" |
508 | // What is "eax_clobbered"? gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html: | ||
509 | // "Do not modify the contents of input-only operands (except for inputs tied | ||
510 | // to outputs). The compiler assumes that on exit from the asm statement these | ||
511 | // operands contain the same values as they had before executing the statement. | ||
512 | // It is not possible to use clobbers to inform the compiler that the values | ||
513 | // in these inputs are changing. One common work-around is to tie the changing | ||
514 | // input variable to an output variable that never gets used." | ||
488 | ); | 515 | ); |
489 | //////////////////////// | 516 | //////////////////////// |
490 | j--; | 517 | j--; |
@@ -500,15 +527,20 @@ static void sp_256to512_mul_8(sp_digit* r, const sp_digit* a, const sp_digit* b) | |||
500 | const uint64_t* bb = (const void*)b; | 527 | const uint64_t* bb = (const void*)b; |
501 | uint64_t* rr = (void*)r; | 528 | uint64_t* rr = (void*)r; |
502 | int k; | 529 | int k; |
503 | uint64_t accl; | 530 | register uint64_t accl asm("r8"); |
504 | uint64_t acch; | 531 | register uint64_t acch asm("r9"); |
532 | /* ^^^ ask gcc to not use rax/rdx/input arg regs for accumulator variables */ | ||
533 | /* (or else it may generate lots of silly mov's and even xchg's!) */ | ||
505 | 534 | ||
506 | acch = accl = 0; | 535 | acch = accl = 0; |
507 | for (k = 0; k < 7; k++) { | 536 | for (k = 0; k < 7; k++) { |
508 | int i, j; | 537 | unsigned i, j; |
509 | uint64_t acc_hi; | 538 | /* ^^^^^ not signed "int", |
539 | * or gcc can use a temp register to sign-extend i,j for aa[i],bb[j] */ | ||
540 | register uint64_t acc_hi asm("r10"); | ||
541 | /* ^^^ ask gcc to not use rax/rdx/input arg regs for accumulators */ | ||
510 | i = k - 3; | 542 | i = k - 3; |
511 | if (i < 0) | 543 | if ((int)i < 0) |
512 | i = 0; | 544 | i = 0; |
513 | j = k - i; | 545 | j = k - i; |
514 | acc_hi = 0; | 546 | acc_hi = 0; |
@@ -516,14 +548,15 @@ static void sp_256to512_mul_8(sp_digit* r, const sp_digit* a, const sp_digit* b) | |||
516 | //////////////////////// | 548 | //////////////////////// |
517 | // uint128_t m = ((uint128_t)a[i]) * b[j]; | 549 | // uint128_t m = ((uint128_t)a[i]) * b[j]; |
518 | // acc_hi:acch:accl += m; | 550 | // acc_hi:acch:accl += m; |
551 | long rax_clobbered; | ||
519 | asm volatile ( | 552 | asm volatile ( |
520 | // aa[i] is already loaded in %%rax | 553 | // aa[i] is already loaded in %%rax |
521 | "\n mulq %7" | 554 | "\n mulq %8" |
522 | "\n addq %%rax, %0" | 555 | "\n addq %%rax, %0" |
523 | "\n adcq %%rdx, %1" | 556 | "\n adcq %%rdx, %1" |
524 | "\n adcq $0, %2" | 557 | "\n adcq $0x0, %2" |
525 | : "=rm" (accl), "=rm" (acch), "=rm" (acc_hi) | 558 | : "=rm" (accl), "=rm" (acch), "=rm" (acc_hi), "=a" (rax_clobbered) |
526 | : "0" (accl), "1" (acch), "2" (acc_hi), "a" (aa[i]), "m" (bb[j]) | 559 | : "0" (accl), "1" (acch), "2" (acc_hi), "3" (aa[i]), "m" (bb[j]) |
527 | : "cc", "dx" | 560 | : "cc", "dx" |
528 | ); | 561 | ); |
529 | //////////////////////// | 562 | //////////////////////// |
diff --git a/shell/ash.c b/shell/ash.c index 719f722a2..7a9f20ec0 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -758,10 +758,9 @@ static void trace_vprintf(const char *fmt, va_list va); | |||
758 | #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) | 758 | #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) |
759 | 759 | ||
760 | static int | 760 | static int |
761 | isdigit_str9(const char *str) | 761 | isdigit_str(const char *str) |
762 | { | 762 | { |
763 | int maxlen = 9 + 1; /* max 9 digits: 999999999 */ | 763 | while (isdigit(*str)) |
764 | while (--maxlen && isdigit(*str)) | ||
765 | str++; | 764 | str++; |
766 | return (*str == '\0'); | 765 | return (*str == '\0'); |
767 | } | 766 | } |
@@ -6386,10 +6385,13 @@ dup2_or_raise(int from, int to) | |||
6386 | newfd = (from != to) ? dup2(from, to) : to; | 6385 | newfd = (from != to) ? dup2(from, to) : to; |
6387 | if (newfd < 0) { | 6386 | if (newfd < 0) { |
6388 | /* Happens when source fd is not open: try "echo >&99" */ | 6387 | /* Happens when source fd is not open: try "echo >&99" */ |
6389 | ash_msg_and_raise_perror("%d", from); | 6388 | ash_msg_and_raise_perror("dup2(%d,%d)", from, to); |
6390 | } | 6389 | } |
6391 | return newfd; | 6390 | return newfd; |
6392 | } | 6391 | } |
6392 | /* The only possible error return is EBADF (fd wasn't open). | ||
6393 | * Transient errors retry, other errors raise exception. | ||
6394 | */ | ||
6393 | static int | 6395 | static int |
6394 | dup_CLOEXEC(int fd, int avoid_fd) | 6396 | dup_CLOEXEC(int fd, int avoid_fd) |
6395 | { | 6397 | { |
@@ -6404,6 +6406,16 @@ dup_CLOEXEC(int fd, int avoid_fd) | |||
6404 | goto repeat; | 6406 | goto repeat; |
6405 | if (errno == EINTR) | 6407 | if (errno == EINTR) |
6406 | goto repeat; | 6408 | goto repeat; |
6409 | if (errno != EBADF) { | ||
6410 | /* "echo >&9999" gets EINVAL trying to save fd 1 to above 9999. | ||
6411 | * We could try saving it _below_ 9999 instead (how?), but | ||
6412 | * this probably means that dup2(9999,1) to effectuate >&9999 | ||
6413 | * would also not work: fd 9999 can't exist. Gracefully bail out. | ||
6414 | * (This differs from "echo >&99" where saving works, but | ||
6415 | * subsequent dup2(99,1) fails if fd 99 is not open). | ||
6416 | */ | ||
6417 | ash_msg_and_raise_perror("fcntl(%d,F_DUPFD,%d)", fd, avoid_fd + 1); | ||
6418 | } | ||
6407 | } | 6419 | } |
6408 | return newfd; | 6420 | return newfd; |
6409 | } | 6421 | } |
@@ -6519,7 +6531,7 @@ save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq) | |||
6519 | new_fd = dup_CLOEXEC(fd, avoid_fd); | 6531 | new_fd = dup_CLOEXEC(fd, avoid_fd); |
6520 | sq->two_fd[i].moved_to = new_fd; | 6532 | sq->two_fd[i].moved_to = new_fd; |
6521 | TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd)); | 6533 | TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd)); |
6522 | if (new_fd < 0) /* what? */ | 6534 | if (new_fd < 0) /* EBADF? what? */ |
6523 | xfunc_die(); | 6535 | xfunc_die(); |
6524 | return 0; /* "we did not close fd" */ | 6536 | return 0; /* "we did not close fd" */ |
6525 | } | 6537 | } |
@@ -6534,8 +6546,7 @@ save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq) | |||
6534 | new_fd = dup_CLOEXEC(fd, avoid_fd); | 6546 | new_fd = dup_CLOEXEC(fd, avoid_fd); |
6535 | TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd)); | 6547 | TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd)); |
6536 | if (new_fd < 0) { | 6548 | if (new_fd < 0) { |
6537 | if (errno != EBADF) | 6549 | /* EBADF (fd is not open) */ |
6538 | xfunc_die(); | ||
6539 | /* new_fd = CLOSED; - already is -1 */ | 6550 | /* new_fd = CLOSED; - already is -1 */ |
6540 | } | 6551 | } |
6541 | sq->two_fd[i].moved_to = new_fd; | 6552 | sq->two_fd[i].moved_to = new_fd; |
@@ -10767,7 +10778,7 @@ expredir(union node *n) | |||
10767 | if (fn.list == NULL) | 10778 | if (fn.list == NULL) |
10768 | ash_msg_and_raise_error("redir error"); | 10779 | ash_msg_and_raise_error("redir error"); |
10769 | #if BASH_REDIR_OUTPUT | 10780 | #if BASH_REDIR_OUTPUT |
10770 | if (!isdigit_str9(fn.list->text)) { | 10781 | if (!isdigit_str(fn.list->text)) { |
10771 | /* >&file, not >&fd */ | 10782 | /* >&file, not >&fd */ |
10772 | if (redir->nfile.fd != 1) /* 123>&file - BAD */ | 10783 | if (redir->nfile.fd != 1) /* 123>&file - BAD */ |
10773 | ash_msg_and_raise_error("redir error"); | 10784 | ash_msg_and_raise_error("redir error"); |
@@ -13284,12 +13295,19 @@ fixredir(union node *n, const char *text, int err) | |||
13284 | if (!err) | 13295 | if (!err) |
13285 | n->ndup.vname = NULL; | 13296 | n->ndup.vname = NULL; |
13286 | 13297 | ||
13298 | if (LONE_DASH(text)) { | ||
13299 | n->ndup.dupfd = -1; | ||
13300 | return; | ||
13301 | } | ||
13302 | |||
13287 | fd = bb_strtou(text, NULL, 10); | 13303 | fd = bb_strtou(text, NULL, 10); |
13288 | if (!errno && fd >= 0) | 13304 | if (!errno && fd >= 0) |
13289 | n->ndup.dupfd = fd; | 13305 | n->ndup.dupfd = fd; |
13290 | else if (LONE_DASH(text)) | ||
13291 | n->ndup.dupfd = -1; | ||
13292 | else { | 13306 | else { |
13307 | /* This also fails on very large numbers | ||
13308 | * which overflow "int" - bb_strtou() does not | ||
13309 | * silently truncate results to word width. | ||
13310 | */ | ||
13293 | if (err) | 13311 | if (err) |
13294 | raise_error_syntax("bad fd number"); | 13312 | raise_error_syntax("bad fd number"); |
13295 | n->ndup.vname = makename(); | 13313 | n->ndup.vname = makename(); |
@@ -13975,7 +13993,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
13975 | if ((c == '>' || c == '<' IF_BASH_REDIR_OUTPUT( || c == 0x100 + '>')) | 13993 | if ((c == '>' || c == '<' IF_BASH_REDIR_OUTPUT( || c == 0x100 + '>')) |
13976 | && quotef == 0 | 13994 | && quotef == 0 |
13977 | ) { | 13995 | ) { |
13978 | if (isdigit_str9(out)) { | 13996 | if (isdigit_str(out)) { |
13979 | PARSEREDIR(); /* passed as params: out, c */ | 13997 | PARSEREDIR(); /* passed as params: out, c */ |
13980 | lasttoken = TREDIR; | 13998 | lasttoken = TREDIR; |
13981 | return lasttoken; | 13999 | return lasttoken; |
diff --git a/shell/ash_test/ash-redir/redir3.right b/shell/ash_test/ash-redir/redir3.right index fd641a8ea..d9cf5dac5 100644 --- a/shell/ash_test/ash-redir/redir3.right +++ b/shell/ash_test/ash-redir/redir3.right | |||
@@ -1,3 +1,3 @@ | |||
1 | TEST | 1 | TEST |
2 | ./redir3.tests: line 4: 9: Bad file descriptor | 2 | ./redir3.tests: line 4: dup2(9,1): Bad file descriptor |
3 | Output to fd#9: 1 | 3 | Output to fd#9: 1 |
diff --git a/shell/ash_test/ash-redir/redir_exec2.right b/shell/ash_test/ash-redir/redir_exec2.right new file mode 100644 index 000000000..2bdc093c9 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_exec2.right | |||
@@ -0,0 +1,4 @@ | |||
1 | fd/5 | ||
2 | fd/4 | ||
3 | fd/3 | ||
4 | One:1 | ||
diff --git a/shell/ash_test/ash-redir/redir_exec2.tests b/shell/ash_test/ash-redir/redir_exec2.tests new file mode 100755 index 000000000..0af767592 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_exec2.tests | |||
@@ -0,0 +1,10 @@ | |||
1 | cd /proc/$$ | ||
2 | exec 5>/dev/null | ||
3 | exec 4>/dev/null | ||
4 | exec 3>/dev/null | ||
5 | ls -1 fd/5 | ||
6 | ls -1 fd/4 | ||
7 | ls -1 fd/3 | ||
8 | exec 5>&- | ||
9 | test -e fd/5 && echo BUG | ||
10 | echo One:$? | ||
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd255.right b/shell/ash_test/ash-redir/redir_to_bad_fd255.right index 9c5e35b36..ca4df91ac 100644 --- a/shell/ash_test/ash-redir/redir_to_bad_fd255.right +++ b/shell/ash_test/ash-redir/redir_to_bad_fd255.right | |||
@@ -1,2 +1,2 @@ | |||
1 | ./redir_to_bad_fd255.tests: line 2: 255: Bad file descriptor | 1 | ./redir_to_bad_fd255.tests: line 2: dup2(255,1): Bad file descriptor |
2 | OK | 2 | OK |
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd3.right b/shell/ash_test/ash-redir/redir_to_bad_fd3.right index 895a4a0a6..5120322a5 100644 --- a/shell/ash_test/ash-redir/redir_to_bad_fd3.right +++ b/shell/ash_test/ash-redir/redir_to_bad_fd3.right | |||
@@ -1,2 +1,2 @@ | |||
1 | ./redir_to_bad_fd3.tests: line 2: 3: Bad file descriptor | 1 | ./redir_to_bad_fd3.tests: line 2: dup2(3,1): Bad file descriptor |
2 | OK | 2 | OK |
diff --git a/shell/hush.c b/shell/hush.c index 3e6a13b32..707b77c9c 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -1575,12 +1575,22 @@ static int dup_CLOEXEC(int fd, int avoid_fd) | |||
1575 | newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1); | 1575 | newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1); |
1576 | if (newfd >= 0) { | 1576 | if (newfd >= 0) { |
1577 | if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */ | 1577 | if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */ |
1578 | fcntl(newfd, F_SETFD, FD_CLOEXEC); | 1578 | close_on_exec_on(newfd); |
1579 | } else { /* newfd < 0 */ | 1579 | } else { /* newfd < 0 */ |
1580 | if (errno == EBUSY) | 1580 | if (errno == EBUSY) |
1581 | goto repeat; | 1581 | goto repeat; |
1582 | if (errno == EINTR) | 1582 | if (errno == EINTR) |
1583 | goto repeat; | 1583 | goto repeat; |
1584 | if (errno != EBADF) { | ||
1585 | /* "echo >&9999" gets EINVAL trying to save fd 1 to above 9999. | ||
1586 | * We could try saving it _below_ 9999 instead (how?), but | ||
1587 | * this probably means that dup2(9999,1) to effectuate >&9999 | ||
1588 | * would also not work: fd 9999 can't exist. | ||
1589 | * (This differs from "echo >&99" where saving works, but | ||
1590 | * subsequent dup2(99,1) fails if fd 99 is not open). | ||
1591 | */ | ||
1592 | bb_perror_msg("fcntl(%d,F_DUPFD,%d)", fd, avoid_fd + 1); | ||
1593 | } | ||
1584 | } | 1594 | } |
1585 | return newfd; | 1595 | return newfd; |
1586 | } | 1596 | } |
@@ -1601,7 +1611,7 @@ static int xdup_CLOEXEC_and_close(int fd, int avoid_fd) | |||
1601 | xfunc_die(); | 1611 | xfunc_die(); |
1602 | } | 1612 | } |
1603 | if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */ | 1613 | if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */ |
1604 | fcntl(newfd, F_SETFD, FD_CLOEXEC); | 1614 | close_on_exec_on(newfd); |
1605 | close(fd); | 1615 | close(fd); |
1606 | return newfd; | 1616 | return newfd; |
1607 | } | 1617 | } |
@@ -7893,10 +7903,16 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
7893 | if (sq) for (; sq[i].orig_fd >= 0; i++) { | 7903 | if (sq) for (; sq[i].orig_fd >= 0; i++) { |
7894 | /* If we collide with an already moved fd... */ | 7904 | /* If we collide with an already moved fd... */ |
7895 | if (fd == sq[i].moved_to) { | 7905 | if (fd == sq[i].moved_to) { |
7896 | sq[i].moved_to = dup_CLOEXEC(sq[i].moved_to, avoid_fd); | 7906 | moved_to = dup_CLOEXEC(sq[i].moved_to, avoid_fd); |
7897 | debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to); | 7907 | debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, moved_to); |
7898 | if (sq[i].moved_to < 0) /* what? */ | 7908 | if (moved_to < 0) { |
7899 | xfunc_die(); | 7909 | /* "echo 2>/dev/tty 10>&9999" testcase: |
7910 | * We move fd 2 to 10, then discover we need to move fd 10 | ||
7911 | * (and not hit 9999) and the latter fails. | ||
7912 | */ | ||
7913 | return NULL; /* fcntl failed */ | ||
7914 | } | ||
7915 | sq[i].moved_to = moved_to; | ||
7900 | return sq; | 7916 | return sq; |
7901 | } | 7917 | } |
7902 | if (fd == sq[i].orig_fd) { | 7918 | if (fd == sq[i].orig_fd) { |
@@ -7910,7 +7926,7 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
7910 | moved_to = dup_CLOEXEC(fd, avoid_fd); | 7926 | moved_to = dup_CLOEXEC(fd, avoid_fd); |
7911 | debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to); | 7927 | debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to); |
7912 | if (moved_to < 0 && errno != EBADF) | 7928 | if (moved_to < 0 && errno != EBADF) |
7913 | xfunc_die(); | 7929 | return NULL; /* fcntl failed (not because fd is closed) */ |
7914 | return append_squirrel(sq, i, fd, moved_to); | 7930 | return append_squirrel(sq, i, fd, moved_to); |
7915 | } | 7931 | } |
7916 | 7932 | ||
@@ -7943,6 +7959,8 @@ static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd) | |||
7943 | */ | 7959 | */ |
7944 | static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | 7960 | static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) |
7945 | { | 7961 | { |
7962 | struct squirrel *new_squirrel; | ||
7963 | |||
7946 | if (avoid_fd < 9) /* the important case here is that it can be -1 */ | 7964 | if (avoid_fd < 9) /* the important case here is that it can be -1 */ |
7947 | avoid_fd = 9; | 7965 | avoid_fd = 9; |
7948 | 7966 | ||
@@ -8006,7 +8024,10 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | |||
8006 | } | 8024 | } |
8007 | 8025 | ||
8008 | /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */ | 8026 | /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */ |
8009 | *sqp = add_squirrel(*sqp, fd, avoid_fd); | 8027 | new_squirrel = add_squirrel(*sqp, fd, avoid_fd); |
8028 | if (!new_squirrel) | ||
8029 | return -1; /* redirect error */ | ||
8030 | *sqp = new_squirrel; | ||
8010 | return 0; /* "we did not close fd" */ | 8031 | return 0; /* "we did not close fd" */ |
8011 | } | 8032 | } |
8012 | 8033 | ||
@@ -8077,8 +8098,11 @@ static int internally_opened_fd(int fd, struct squirrel *sq) | |||
8077 | return 0; | 8098 | return 0; |
8078 | } | 8099 | } |
8079 | 8100 | ||
8080 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, | 8101 | /* sqp != NULL means we squirrel away copies of stdin, stdout, |
8081 | * and stderr if they are redirected. */ | 8102 | * and stderr if they are redirected. |
8103 | * If redirection fails, return 1. This will make caller | ||
8104 | * skip command execution and restore already created redirect fds. | ||
8105 | */ | ||
8082 | static int setup_redirects(struct command *prog, struct squirrel **sqp) | 8106 | static int setup_redirects(struct command *prog, struct squirrel **sqp) |
8083 | { | 8107 | { |
8084 | struct redir_struct *redir; | 8108 | struct redir_struct *redir; |
@@ -8089,7 +8113,8 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
8089 | 8113 | ||
8090 | if (redir->rd_type == REDIRECT_HEREDOC2) { | 8114 | if (redir->rd_type == REDIRECT_HEREDOC2) { |
8091 | /* "rd_fd<<HERE" case */ | 8115 | /* "rd_fd<<HERE" case */ |
8092 | save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); | 8116 | if (save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp) < 0) |
8117 | return 1; | ||
8093 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ | 8118 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ |
8094 | * of the heredoc */ | 8119 | * of the heredoc */ |
8095 | debug_printf_redir("set heredoc '%s'\n", | 8120 | debug_printf_redir("set heredoc '%s'\n", |
@@ -8109,7 +8134,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
8109 | * "cmd > <file" (2nd redirect starts too early) | 8134 | * "cmd > <file" (2nd redirect starts too early) |
8110 | */ | 8135 | */ |
8111 | syntax_error("invalid redirect"); | 8136 | syntax_error("invalid redirect"); |
8112 | continue; | 8137 | return 1; |
8113 | } | 8138 | } |
8114 | mode = redir_table[redir->rd_type].mode; | 8139 | mode = redir_table[redir->rd_type].mode; |
8115 | p = expand_string_to_string(redir->rd_filename, | 8140 | p = expand_string_to_string(redir->rd_filename, |
@@ -8124,7 +8149,9 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
8124 | */ | 8149 | */ |
8125 | return 1; | 8150 | return 1; |
8126 | } | 8151 | } |
8127 | if (newfd == redir->rd_fd && sqp) { | 8152 | if (newfd == redir->rd_fd && sqp |
8153 | && sqp != ERR_PTR /* not a redirect in "exec" */ | ||
8154 | ) { | ||
8128 | /* open() gave us precisely the fd we wanted. | 8155 | /* open() gave us precisely the fd we wanted. |
8129 | * This means that this fd was not busy | 8156 | * This means that this fd was not busy |
8130 | * (not opened to anywhere). | 8157 | * (not opened to anywhere). |
@@ -8146,6 +8173,8 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
8146 | /* if "N>&-": close redir->rd_fd (newfd is REDIRFD_CLOSE) */ | 8173 | /* if "N>&-": close redir->rd_fd (newfd is REDIRFD_CLOSE) */ |
8147 | 8174 | ||
8148 | closed = save_fd_on_redirect(redir->rd_fd, /*avoid:*/ newfd, sqp); | 8175 | closed = save_fd_on_redirect(redir->rd_fd, /*avoid:*/ newfd, sqp); |
8176 | if (closed < 0) | ||
8177 | return 1; /* error */ | ||
8149 | if (newfd == REDIRFD_CLOSE) { | 8178 | if (newfd == REDIRFD_CLOSE) { |
8150 | /* "N>&-" means "close me" */ | 8179 | /* "N>&-" means "close me" */ |
8151 | if (!closed) { | 8180 | if (!closed) { |
@@ -8159,13 +8188,16 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
8159 | * and second redirect closes 3! Restore code then closes 3 again. | 8188 | * and second redirect closes 3! Restore code then closes 3 again. |
8160 | */ | 8189 | */ |
8161 | } else { | 8190 | } else { |
8162 | /* if newfd is a script fd or saved fd, simulate EBADF */ | 8191 | /* if newfd is a script fd or saved fd, do not allow to use it */ |
8163 | if (internally_opened_fd(newfd, sqp && sqp != ERR_PTR ? *sqp : NULL)) { | 8192 | if (internally_opened_fd(newfd, sqp && sqp != ERR_PTR ? *sqp : NULL)) { |
8164 | //errno = EBADF; | 8193 | bb_error_msg("fd#%d is not open", newfd); |
8165 | //bb_perror_msg_and_die("can't duplicate file descriptor"); | 8194 | return 1; |
8166 | newfd = -1; /* same effect as code above */ | 8195 | } |
8196 | if (dup2(newfd, redir->rd_fd) < 0) { | ||
8197 | /* "echo >&99" testcase */ | ||
8198 | bb_perror_msg("dup2(%d,%d)", newfd, redir->rd_fd); | ||
8199 | return 1; | ||
8167 | } | 8200 | } |
8168 | xdup2(newfd, redir->rd_fd); | ||
8169 | if (redir->rd_dup == REDIRFD_TO_FILE) | 8201 | if (redir->rd_dup == REDIRFD_TO_FILE) |
8170 | /* "rd_fd > FILE" */ | 8202 | /* "rd_fd > FILE" */ |
8171 | close(newfd); | 8203 | close(newfd); |
@@ -10710,7 +10742,7 @@ int hush_main(int argc, char **argv) | |||
10710 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); | 10742 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); |
10711 | if (G_interactive_fd < 0) { | 10743 | if (G_interactive_fd < 0) { |
10712 | /* try to dup to any fd */ | 10744 | /* try to dup to any fd */ |
10713 | G_interactive_fd = dup(STDIN_FILENO); | 10745 | G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1); |
10714 | if (G_interactive_fd < 0) { | 10746 | if (G_interactive_fd < 0) { |
10715 | /* give up */ | 10747 | /* give up */ |
10716 | G_interactive_fd = 0; | 10748 | G_interactive_fd = 0; |
@@ -10720,8 +10752,6 @@ int hush_main(int argc, char **argv) | |||
10720 | } | 10752 | } |
10721 | debug_printf("interactive_fd:%d\n", G_interactive_fd); | 10753 | debug_printf("interactive_fd:%d\n", G_interactive_fd); |
10722 | if (G_interactive_fd) { | 10754 | if (G_interactive_fd) { |
10723 | close_on_exec_on(G_interactive_fd); | ||
10724 | |||
10725 | if (G_saved_tty_pgrp) { | 10755 | if (G_saved_tty_pgrp) { |
10726 | /* If we were run as 'hush &', sleep until we are | 10756 | /* If we were run as 'hush &', sleep until we are |
10727 | * in the foreground (tty pgrp == our pgrp). | 10757 | * in the foreground (tty pgrp == our pgrp). |
@@ -10796,9 +10826,6 @@ int hush_main(int argc, char **argv) | |||
10796 | G_interactive_fd = 0; | 10826 | G_interactive_fd = 0; |
10797 | } | 10827 | } |
10798 | } | 10828 | } |
10799 | if (G_interactive_fd) { | ||
10800 | close_on_exec_on(G_interactive_fd); | ||
10801 | } | ||
10802 | install_special_sighandlers(); | 10829 | install_special_sighandlers(); |
10803 | #else | 10830 | #else |
10804 | /* We have interactiveness code disabled */ | 10831 | /* We have interactiveness code disabled */ |
diff --git a/shell/hush_test/hush-redir/redir3.right b/shell/hush_test/hush-redir/redir3.right index e3c878b7d..d49237c42 100644 --- a/shell/hush_test/hush-redir/redir3.right +++ b/shell/hush_test/hush-redir/redir3.right | |||
@@ -1,2 +1,3 @@ | |||
1 | TEST | 1 | TEST |
2 | hush: can't duplicate file descriptor: Bad file descriptor | 2 | hush: dup2(9,1): Bad file descriptor |
3 | Output to fd#9: 1 | ||
diff --git a/shell/hush_test/hush-redir/redir_exec2.right b/shell/hush_test/hush-redir/redir_exec2.right new file mode 100644 index 000000000..2bdc093c9 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_exec2.right | |||
@@ -0,0 +1,4 @@ | |||
1 | fd/5 | ||
2 | fd/4 | ||
3 | fd/3 | ||
4 | One:1 | ||
diff --git a/shell/hush_test/hush-redir/redir_exec2.tests b/shell/hush_test/hush-redir/redir_exec2.tests new file mode 100755 index 000000000..0af767592 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_exec2.tests | |||
@@ -0,0 +1,10 @@ | |||
1 | cd /proc/$$ | ||
2 | exec 5>/dev/null | ||
3 | exec 4>/dev/null | ||
4 | exec 3>/dev/null | ||
5 | ls -1 fd/5 | ||
6 | ls -1 fd/4 | ||
7 | ls -1 fd/3 | ||
8 | exec 5>&- | ||
9 | test -e fd/5 && echo BUG | ||
10 | echo One:$? | ||
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd.right b/shell/hush_test/hush-redir/redir_to_bad_fd.right index 936911ce5..4fc51cc93 100644 --- a/shell/hush_test/hush-redir/redir_to_bad_fd.right +++ b/shell/hush_test/hush-redir/redir_to_bad_fd.right | |||
@@ -1 +1,2 @@ | |||
1 | hush: can't duplicate file descriptor: Bad file descriptor | 1 | hush: dup2(10,1): Bad file descriptor |
2 | OK | ||
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd255.right b/shell/hush_test/hush-redir/redir_to_bad_fd255.right index 936911ce5..baa2959ca 100644 --- a/shell/hush_test/hush-redir/redir_to_bad_fd255.right +++ b/shell/hush_test/hush-redir/redir_to_bad_fd255.right | |||
@@ -1 +1,2 @@ | |||
1 | hush: can't duplicate file descriptor: Bad file descriptor | 1 | hush: dup2(255,1): Bad file descriptor |
2 | OK | ||
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd3.right b/shell/hush_test/hush-redir/redir_to_bad_fd3.right index 936911ce5..5e54abc0a 100644 --- a/shell/hush_test/hush-redir/redir_to_bad_fd3.right +++ b/shell/hush_test/hush-redir/redir_to_bad_fd3.right | |||
@@ -1 +1,2 @@ | |||
1 | hush: can't duplicate file descriptor: Bad file descriptor | 1 | hush: fd#3 is not open |
2 | OK | ||