Fix various ftw/nftw "shall fail"s from POSIX.

POSIX says ftw/nftw "shall fail" in various cases where BSD's fts_open
doesn't. Since our ftw/nftw are written in terms of fts_open, add a back
door so we can hint to ourselves when we should have the POSIX semantics.

Also pull several O_CLOEXEC and don't-null-check-before-free cleanups
from upstream, and add a couple of tests.

Bug: http://b/31152735
Test: ran bionic tests and LTP "nftw01" test
Change-Id: Ib05facacc1da4c8b2ab48e9ecce88f11a5406630
diff --git a/libc/bionic/fts.c b/libc/bionic/fts.c
index a43c8c9..8888ab1 100644
--- a/libc/bionic/fts.c
+++ b/libc/bionic/fts.c
@@ -70,22 +70,13 @@
 #define	BNAMES		2		/* fts_children, names only */
 #define	BREAD		3		/* fts_read */
 
-FTS *
-fts_open(char * const *argv, int options,
-    int (*compar)(const FTSENT **, const FTSENT **))
-{
+FTS* __fts_open(char* const* argv, int options, int (*compar)(const FTSENT**, const FTSENT**)) {
 	FTS *sp;
 	FTSENT *p, *root;
 	int nitems;
 	FTSENT *parent, *tmp;
 	size_t len;
 
-	/* Options check. */
-	if (options & ~FTS_OPTIONMASK) {
-		errno = EINVAL;
-		return (NULL);
-	}
-
 	/* Allocate/initialize the stream */
 	if ((sp = calloc(1, sizeof(FTS))) == NULL)
 		return (NULL);
@@ -123,6 +114,9 @@
 		p->fts_accpath = p->fts_name;
 		p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), -1);
 
+		// For ftw/nftw we need to fail early: http://b/31152735
+		if ((options & FTS_FOR_FTW) != 0 && p->fts_info == FTS_NS) goto mem3;
+
 		/* Command-line "." and ".." are real directories. */
 		if (p->fts_info == FTS_DOT)
 			p->fts_info = FTS_D;
@@ -164,7 +158,7 @@
 	 * and ".." are all fairly nasty problems.  Note, if we can't get the
 	 * descriptor we run anyway, just more slowly.
 	 */
-	if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0)
+	if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY|O_CLOEXEC, 0)) < 0)
 		SET(FTS_NOCHDIR);
 
 	if (nitems == 0)
@@ -227,10 +221,8 @@
 	rfd = ISSET(FTS_NOCHDIR) ? -1 : sp->fts_rfd;
 
 	/* Free up child linked list, sort array, path buffer, stream ptr.*/
-	if (sp->fts_child)
-		fts_lfree(sp->fts_child);
-	if (sp->fts_array)
-		free(sp->fts_array);
+	fts_lfree(sp->fts_child);
+	free(sp->fts_array);
 	free(sp->fts_path);
 	free(sp);
 
@@ -289,7 +281,7 @@
 	    (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
 		p->fts_info = fts_stat(sp, p, 1, -1);
 		if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
-			if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) {
+			if ((p->fts_symfd = open(".", O_RDONLY|O_CLOEXEC, 0)) < 0) {
 				p->fts_errno = errno;
 				p->fts_info = FTS_ERR;
 			} else
@@ -378,8 +370,7 @@
 		if (p->fts_instr == FTS_FOLLOW) {
 			p->fts_info = fts_stat(sp, p, 1, -1);
 			if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
-				if ((p->fts_symfd =
-				    open(".", O_RDONLY, 0)) < 0) {
+				if ((p->fts_symfd = open(".", O_RDONLY|O_CLOEXEC, 0)) < 0) {
 					p->fts_errno = errno;
 					p->fts_info = FTS_ERR;
 				} else
@@ -498,8 +489,7 @@
 		return (NULL);
 
 	/* Free up any previous child list. */
-	if (sp->fts_child)
-		fts_lfree(sp->fts_child);
+	fts_lfree(sp->fts_child);
 
 	if (instr == FTS_NAMEONLY) {
 		SET(FTS_NAMEONLY);
@@ -518,7 +508,7 @@
 	    ISSET(FTS_NOCHDIR))
 		return (sp->fts_child = fts_build(sp, instr));
 
-	if ((fd = open(".", O_RDONLY, 0)) < 0)
+	if ((fd = open(".", O_RDONLY|O_CLOEXEC, 0)) < 0)
 		return (NULL);
 	sp->fts_child = fts_build(sp, instr);
 	if (fchdir(fd)) {
@@ -664,8 +654,7 @@
 				 * structures already allocated.
 				 */
 mem1:				saved_errno = errno;
-				if (p)
-					free(p);
+				free(p);
 				fts_lfree(head);
 				(void)closedir(dirp);
 				cur->fts_info = FTS_ERR;
@@ -817,9 +806,9 @@
 	 * fail, set the errno from the stat call.
 	 */
 	if (ISSET(FTS_LOGICAL) || follow) {
-		if (fstatat(dfd, path, sbp, 0)) {
+		if (fstatat(dfd, path, sbp, 0) == -1) {
 			saved_errno = errno;
-			if (!fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
+			if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW) == 0) {
 				errno = 0;
 				return (FTS_SLNONE);
 			}
@@ -886,8 +875,7 @@
 		sp->fts_nitems = nitems + 40;
 		if ((a = reallocarray(sp->fts_array,
 		    sp->fts_nitems, sizeof(FTSENT *))) == NULL) {
-			if (sp->fts_array)
-				free(sp->fts_array);
+			free(sp->fts_array);
 			sp->fts_array = NULL;
 			sp->fts_nitems = 0;
 			return (head);
@@ -961,8 +949,7 @@
 	 */
 	more += 256;
 	if (sp->fts_pathlen + more < sp->fts_pathlen) {
-		if (sp->fts_path)
-			free(sp->fts_path);
+		free(sp->fts_path);
 		sp->fts_path = NULL;
 		errno = ENAMETOOLONG;
 		return (1);
@@ -970,8 +957,7 @@
 	sp->fts_pathlen += more;
 	p = realloc(sp->fts_path, sp->fts_pathlen);
 	if (p == NULL) {
-		if (sp->fts_path)
-			free(sp->fts_path);
+		free(sp->fts_path);
 		sp->fts_path = NULL;
 		return (1);
 	}
@@ -1032,7 +1018,7 @@
 	newfd = fd;
 	if (ISSET(FTS_NOCHDIR))
 		return (0);
-	if (fd < 0 && (newfd = open(path, O_RDONLY, 0)) < 0)
+	if (fd < 0 && (newfd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0)) < 0)
 		return (-1);
 	if (fstat(newfd, &sb)) {
 		ret = -1;
@@ -1051,3 +1037,12 @@
 	errno = oerrno;
 	return (ret);
 }
+
+FTS* fts_open(char* const* argv, int options, int (*compar)(const FTSENT**, const FTSENT**)) {
+    // Options check.
+    if ((options & ~FTS_OPTIONMASK) != 0) {
+        errno = EINVAL;
+        return NULL;
+    }
+    return __fts_open(argv, options, compar);
+}