Date: Thu, 4 Sep 1997 11:20:48 +1000 From: Bruce Evans <bde@zeta.org.au> To: bde@zeta.org.au, bugs@FreeBSD.ORG Subject: Re: fixes for execlp() and execvp() Message-ID: <199709040120.LAA17438@godzilla.zeta.org.au>
next in thread | raw e-mail | index | archive | help
I wrote: >Log: >Handle execve() failure in execlp() and execvp() the same (*) as in sh(1): >- don't retry for ETXTBSY. >- continue with the next component of the search path after most errors. > Previously we returned after most errors. This change is significant > for at least the following errors: >... >It is bogus to keep searching after finding an accessible executable, but >that's what sh(1) does and what POSIX.2 seems to require. On more careful reading, POSIX.2 seems to say that the search stops after finding "the name with the execute permissions set that would allow the process to execute it". This seems to require an implementation that stats each file until it finds one with suitable execute permissions bits set, and then attempts to exec just once. The usual implementation which attempts to exec each file (to avoid races) is wrong because it is too sophisticated - it notices things that stat() can't notice: 1. files that have suitable permissions but are on a MNT_NOEXEC file system are not executable 2. special files are not executable (POSIX.2 may mention this, but I can't see where) 3. files with bogus interpreters, e.g., one containing "#!/bin/zz". execve() returns ENOENT for this (if its permissions allow execution). >(*) sh(1) currently sets $? wrong after finding an inaccessible executable. >EACCES should be sticky like it already was for execlp(). This is wrong too. sh(1)'s handling of errno is close to what is required for setting the status POSIXly (127 = command not found; 126 = command found, but can not be executed due to non-accessibility or error exec'ing it). Here is another attempt at fixing execlp() and execvp(). It follows historical practice again except for not retrying ETXTBSY, being more careful about ambiguity of EACCES and some other errnos, and not returning spurious errnos that may occur during the search (if nothing is found, just return ENOENT). All documented, unambiguous errnos are handled explicitly to avoid the new stat() call. Bruce diff -c2 exec.c~ exec.c *** exec.c~ Wed Nov 20 02:07:18 1996 --- exec.c Thu Sep 4 10:38:33 1997 *************** *** 38,41 **** --- 38,42 ---- #include <sys/param.h> #include <sys/types.h> + #include <sys/stat.h> #include <errno.h> #include <unistd.h> *************** *** 188,195 **** register int cnt, lp, ln; register char *p; ! int eacces, etxtbsy; char *bp, *cur, *path, buf[MAXPATHLEN]; ! eacces = etxtbsy = 0; /* If it's an absolute or relative path name, it's easy. */ --- 189,197 ---- register int cnt, lp, ln; register char *p; ! int eacces, save_errno; char *bp, *cur, *path, buf[MAXPATHLEN]; + struct stat sb; ! eacces = 0; /* If it's an absolute or relative path name, it's easy. */ *************** *** 242,248 **** retry: (void)execve(bp, argv, environ); switch(errno) { ! case EACCES: ! eacces = 1; ! break; case ENOENT: break; --- 244,251 ---- retry: (void)execve(bp, argv, environ); switch(errno) { ! case E2BIG: ! goto done; ! case ELOOP: ! case ENAMETOOLONG: case ENOENT: break; *************** *** 259,267 **** free(memp); goto done; case ETXTBSY: ! if (etxtbsy < 3) ! (void)sleep(++etxtbsy); ! goto retry; default: goto done; } --- 262,290 ---- free(memp); goto done; + case ENOMEM: + goto done; + case ENOTDIR: + break; case ETXTBSY: ! /* ! * We used to retry here, but sh(1) doesn't. ! */ ! goto done; default: + /* + * EACCES may be for an inaccessible directory or + * a non-executable file. Call stat() to decide + * which. This also handles ambiguities for EFAULT + * and EIO, and undocumented errors like ESTALE. + * We hope that the race for a stat() is unimportant. + */ + save_errno = errno; + if (stat(argv[0], &sb) != 0) + break; + if (save_errno == EACCES) { + eacces = 1; + continue; + } + errno = save_errno; goto done; } *************** *** 269,273 **** if (eacces) errno = EACCES; ! else if (!errno) errno = ENOENT; done: if (path) --- 292,296 ---- if (eacces) errno = EACCES; ! else errno = ENOENT; done: if (path)
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199709040120.LAA17438>