Skip to content
This repository was archived by the owner on Jun 16, 2025. It is now read-only.

Commit 3bc5816

Browse files
committed
Fix handling of skipped directories
The bug in `path_opentype()` fixed by this commit may affect other scenarios but we know it affects autoloaded functions. Hence the unit test for that scenario. Fixes #1454
1 parent 5abcbd0 commit 3bc5816

File tree

4 files changed

+40
-7
lines changed

4 files changed

+40
-7
lines changed

src/cmd/ksh93/sh/path.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -479,28 +479,30 @@ Pathcomp_t *path_get(Shell_t *shp, const char *name) {
479479
//
480480
static_fn int path_opentype(Shell_t *shp, const char *name, Pathcomp_t *pp, int fun) {
481481
int fd = -1;
482-
struct stat statb;
483-
Pathcomp_t *oldpp;
484482

485483
if (!pp && !shp->pathlist) path_init(shp);
486484
if (!fun && strchr(name, '/') && sh_isoption(shp, SH_RESTRICTED)) {
487485
errormsg(SH_DICT, ERROR_exit(1), e_restricted, name);
488486
__builtin_unreachable();
489487
}
488+
489+
// The structure of this loop is slightly odd. It's a consequence of how path_nextcomp() works.
490+
Pathcomp_t *next_pp = pp;
490491
do {
491-
pp = path_nextcomp(shp, oldpp = pp, name, 0);
492-
while (oldpp && (oldpp->flags & PATH_SKIP)) oldpp = oldpp->next;
493-
if (fun && (!oldpp || !(oldpp->flags & PATH_FPATH))) continue;
492+
pp = next_pp;
493+
next_pp = path_nextcomp(shp, pp, name, NULL);
494+
if (pp && (pp->flags & PATH_SKIP)) continue;
495+
if (fun && (!pp || !(pp->flags & PATH_FPATH))) continue;
494496
fd = sh_open(path_relative(shp, stkptr(shp->stk, PATH_OFFSET)), O_RDONLY | O_CLOEXEC, 0);
497+
struct stat statb;
495498
if (fd >= 0 && (fstat(fd, &statb) < 0 || S_ISDIR(statb.st_mode))) {
496499
errno = EISDIR;
497500
sh_close(fd);
498501
fd = -1;
499502
}
500-
} while (fd < 0 && pp);
503+
} while (fd < 0 && next_pp);
501504

502505
assert(fd < 0 || sh_iovalidfd(shp, fd));
503-
504506
if (fd >= 0 && (fd = sh_iomovefd(shp, fd)) > 0) {
505507
(void)fcntl(fd, F_SETFD, FD_CLOEXEC);
506508
shp->fdstatus[fd] |= IOCLEX;

src/cmd/ksh93/tests/autoload.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Verify the behavior of autoloaded functions.
2+
3+
# ====================
4+
# Verify that directories in the path search list which should be skipped (e.g., because they don't
5+
# exist) interacts correctly with autoloaded functions.
6+
#
7+
# See https://github.com/att/ast/issues/1454
8+
expect=$"Func cd called with |$TEST_DIR/usr|\n$TEST_DIR/usr"
9+
actual=$($SHELL "$TEST_ROOT/data/skipped_dir")
10+
actual_status=$?
11+
expect_status=0
12+
[[ $actual_status == $expect_status ]] ||
13+
log_error "autoload function skipped dir test wrong status" "$expect_status" "$actual_status"
14+
[[ $actual == $expect ]] ||
15+
log_error "autoload function skipped dir test wrong output" "$expect" "$actual"

src/cmd/ksh93/tests/data/skipped_dir

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# See https://github.com/att/ast/issues/1454
2+
3+
mkdir -p "$TEST_DIR/usr/bin"
4+
print '#!/bin/sh' >"$TEST_DIR/usr/bin/cd"
5+
print 'builtin cd "$@"' >>"$TEST_DIR/usr/bin/cd"
6+
prefix="$TEST_DIR/ksh.$$"
7+
8+
FPATH="$prefix/bad:$prefix/functions"
9+
mkdir -p "$prefix/functions"
10+
print 'function cd { echo "Func cd called with |$*|"; command cd "$@"; }' >"$prefix/functions/cd"
11+
typeset -fu cd
12+
13+
PATH="/arglebargle:$PATH:$TEST_DIR/usr/bin:$TEST_DIR/bin"
14+
cd "$TEST_DIR/usr"
15+
pwd

src/cmd/ksh93/tests/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ all_tests = [
5353
['arrays.sh'],
5454
['arrays2.sh'],
5555
['attributes.sh'],
56+
['autoload.sh'],
5657
['basic.sh', 90],
5758
['bracket.sh'],
5859
['builtins.sh'],

0 commit comments

Comments
 (0)