From owner-freebsd-hackers@FreeBSD.ORG Fri Jan 1 21:25:36 2010 Return-Path: Delivered-To: freebsd-hackers@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id CE4DF106566B for ; Fri, 1 Jan 2010 21:25:36 +0000 (UTC) (envelope-from jilles@stack.nl) Received: from mx1.stack.nl (relay04.stack.nl [IPv6:2001:610:1108:5010::107]) by mx1.freebsd.org (Postfix) with ESMTP id 93C958FC17 for ; Fri, 1 Jan 2010 21:25:36 +0000 (UTC) Received: from toad.stack.nl (toad.stack.nl [IPv6:2001:610:1108:5010::135]) by mx1.stack.nl (Postfix) with ESMTP id EEE481DD625 for ; Fri, 1 Jan 2010 22:25:35 +0100 (CET) Received: by toad.stack.nl (Postfix, from userid 1677) id CD0F173F9D; Fri, 1 Jan 2010 22:25:35 +0100 (CET) Date: Fri, 1 Jan 2010 22:25:35 +0100 From: Jilles Tjoelker To: freebsd-hackers@freebsd.org Message-ID: <20100101212535.GB60021@stack.nl> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="gBBFr7Ir9EOA20Yy" Content-Disposition: inline User-Agent: Mutt/1.5.18 (2008-05-17) Subject: [PATCH] fts(3): allow FTS_LOGICAL without FTS_NOCHDIR X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 01 Jan 2010 21:25:36 -0000 --gBBFr7Ir9EOA20Yy Content-Type: text/plain; charset=us-ascii Content-Disposition: inline fts(3) currently automatically enables FTS_NOCHDIR if FTS_LOGICAL (-L in various utilities) is used, according to a comment because symlinks are too hard. However, the effect of fts_instr=FTS_FOLLOW seems quite similar to FTS_LOGICAL in chdir mode. Using one more bit in fts_flags, I made FTS_LOGICAL work with chdir. This has several advantages: * it runs faster * it eliminates the PATH_MAX restriction on deep directory trees * find -L ... -type l -delete can be made to work without ugliness (although it is of course still insecure if run over directory trees where untrusted users have write access) Question: why are the values for fts_flags in the public header file fts.h, even though they are supposedly private? In case the mailing list eats the patch, it is also available here: http://www.stack.nl/~jilles/unix/fts-logical-chdir.patch -- Jilles Tjoelker --gBBFr7Ir9EOA20Yy Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="fts-logical-chdir.patch" Index: include/fts.h =================================================================== --- include/fts.h (revision 201269) +++ include/fts.h (working copy) @@ -106,6 +106,7 @@ #define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */ #define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here */ #define FTS_ISW 0x04 /* this is a whiteout object */ +#define FTS_DIRSYM 0x08 /* this is a symlink to a directory */ unsigned fts_flags; /* private flags for FTSENT structure */ #define FTS_AGAIN 1 /* read node again */ Index: lib/libc/gen/fts.c =================================================================== --- lib/libc/gen/fts.c (revision 201269) +++ lib/libc/gen/fts.c (working copy) @@ -141,10 +141,6 @@ /* Shush, GCC. */ tmp = NULL; - /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ - if (ISSET(FTS_LOGICAL)) - SET(FTS_NOCHDIR); - /* * Start out with 1K of path space, and enough, in any case, * to hold the user's paths. @@ -355,8 +351,10 @@ /* If skipped or crossed mount point, do post-order visit. */ if (instr == FTS_SKIP || (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { - if (p->fts_flags & FTS_SYMFOLLOW) + if (p->fts_flags & FTS_SYMFOLLOW) { (void)_close(p->fts_symfd); + p->fts_symfd = -1; + } if (sp->fts_child) { fts_lfree(sp->fts_child); sp->fts_child = NULL; @@ -385,6 +383,14 @@ * FTS_STOP or the fts_info field of the node. */ if (sp->fts_child != NULL) { + if (p->fts_flags & FTS_DIRSYM && + !(p->fts_flags & FTS_SYMFOLLOW)) { + if ((p->fts_symfd = _open(".", O_RDONLY, 0)) < 0) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + } else + p->fts_flags |= FTS_SYMFOLLOW; + } if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { p->fts_errno = errno; p->fts_flags |= FTS_DONTCHDIR; @@ -674,8 +680,8 @@ nlinks = 0; /* Be quiet about nostat, GCC. */ nostat = 0; - } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { - if (fts_ufslinks(sp, cur)) + } else if (ISSET(FTS_NOSTAT)) { + if (ISSET(FTS_PHYSICAL) && fts_ufslinks(sp, cur)) nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); else nlinks = -1; @@ -707,7 +713,19 @@ */ cderrno = 0; if (nlinks || type == BREAD) { - if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { + if (cur->fts_flags & FTS_DIRSYM && + !(cur->fts_flags & FTS_SYMFOLLOW)) { + if ((cur->fts_symfd = _open(".", O_RDONLY, 0)) < 0) { + if (nlinks && type == BREAD) + cur->fts_errno = errno; + cur->fts_flags |= FTS_DONTCHDIR; + descend = 0; + cderrno = errno; + } else + cur->fts_flags |= FTS_SYMFOLLOW; + } + if (!(cur->fts_flags & FTS_DONTCHDIR) && + fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { if (nlinks && type == BREAD) cur->fts_errno = errno; cur->fts_flags |= FTS_DONTCHDIR; @@ -796,7 +814,8 @@ } else if (nlinks == 0 #ifdef DT_DIR || (nostat && - dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) + dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN && + (dp->d_type != DT_LNK || ISSET(FTS_PHYSICAL))) #endif ) { p->fts_accpath = @@ -883,7 +902,7 @@ FTSENT *t; dev_t dev; ino_t ino; - struct stat *sbp, sb; + struct stat *sbp, sb, sb2; int saved_errno; /* If user needs stat info, stat buffer already allocated. */ @@ -923,6 +942,20 @@ if (S_ISDIR(sbp->st_mode)) { /* + * Check if this is actually a symlink to a directory. + * If so, record this fact so we save a file descriptor + * to get back instead of using "..". + */ + if (ISSET(FTS_LOGICAL) && !follow) { + if (lstat(p->fts_accpath, &sb2)) { + p->fts_errno = errno; + goto err; + } + if (S_ISLNK(sb2.st_mode)) + p->fts_flags |= FTS_DIRSYM; + } + + /* * Set the device/inode. Used to find cycles and check for * crossing mount points. Also remember the link count, used * in fts_build to limit the number of stat calls. It is --gBBFr7Ir9EOA20Yy--