summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>1997-01-14 01:02:43 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>1997-01-14 01:02:43 +0000
commit4c5a3612a2833dbfa31a2b9b3a5ff43b99b5fc43 (patch)
tree42faa530fdcac02925c54cdf21f82125747ea2b1
parent1b4848cadba7515a0635d851d630007f70858907 (diff)
Be extra careful when chdir'ing to subdirs.
-rw-r--r--lib/libc/gen/fts.c54
1 files changed, 49 insertions, 5 deletions
diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c
index dfb2a30039a..6f9507390cd 100644
--- a/lib/libc/gen/fts.c
+++ b/lib/libc/gen/fts.c
@@ -32,7 +32,7 @@
*/
#if defined(LIBC_SCCS) && !defined(lint)
-static char rcsid[] = "$OpenBSD: fts.c,v 1.4 1996/12/23 06:08:59 millert Exp $";
+static char rcsid[] = "$OpenBSD: fts.c,v 1.5 1997/01/14 01:02:42 millert Exp $";
#endif /* LIBC_SCCS and not lint */
#include <sys/param.h>
@@ -55,6 +55,7 @@ static void fts_padjust __P((FTS *, void *));
static int fts_palloc __P((FTS *, size_t));
static FTSENT *fts_sort __P((FTS *, FTSENT *, int));
static u_short fts_stat __P((FTS *, FTSENT *, int));
+static int fts_safe_changedir __P((FTS *, FTSENT *, int));
#define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2]))
@@ -336,7 +337,7 @@ fts_read(sp)
* FTS_STOP or the fts_info field of the node.
*/
if (sp->fts_child) {
- if (CHDIR(sp, p->fts_accpath)) {
+ if (fts_safe_changedir(sp, p, -1)) {
p->fts_errno = errno;
p->fts_flags |= FTS_DONTCHDIR;
for (p = sp->fts_child; p; p = p->fts_link)
@@ -363,7 +364,7 @@ next: tmp = p;
* load the paths for the next root.
*/
if (p->fts_level == FTS_ROOTLEVEL) {
- if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) {
+ if (FCHDIR(sp, sp->fts_rfd)) {
SET(FTS_STOP);
return (NULL);
}
@@ -419,7 +420,7 @@ name: t = sp->fts_path + NAPPEND(p->fts_parent);
* one directory.
*/
if (p->fts_level == FTS_ROOTLEVEL) {
- if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) {
+ if (FCHDIR(sp, sp->fts_rfd)) {
SET(FTS_STOP);
return (NULL);
}
@@ -621,7 +622,7 @@ fts_build(sp, type)
*/
cderrno = 0;
if (nlinks || type == BREAD)
- if (FCHDIR(sp, dirfd(dirp))) {
+ if (fts_safe_changedir(sp, cur, dirfd(dirp))) {
if (nlinks && type == BREAD)
cur->fts_errno = errno;
cur->fts_flags |= FTS_DONTCHDIR;
@@ -998,3 +999,46 @@ fts_maxarglen(argv)
max = len;
return (max);
}
+
+/*
+ * Change to dir specified by fd or p->fts_accpath without getting
+ * tricked by someone changing the world out from underneath us.
+ * Assumes p->fts_dev and p->fts_ino are filled in.
+ */
+static int
+fts_safe_changedir(sp, p, fd)
+ FTS *sp;
+ FTSENT *p;
+ int fd;
+{
+ int ret, oerrno, newfd = fd;
+ struct stat sb;
+
+ if (ISSET(FTS_NOCHDIR))
+ return (0);
+
+ if (fd < 0 && (newfd = open(p->fts_accpath, O_RDONLY, 0)) < 0)
+ return (-1);
+
+ if (fstat(newfd, &sb)) {
+ ret = -1;
+ goto bail;
+ }
+
+ if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) {
+ errno = ENOENT; /* disinformation */
+ ret = -1;
+ goto bail;
+ }
+
+ ret = fchdir(newfd);
+
+bail:
+ oerrno = errno;
+
+ if (fd < 0)
+ (void) close(newfd);
+
+ errno = oerrno;
+ return(ret);
+}