summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>2006-05-15 10:59:10 +0000
committerJim Meyering <jim@meyering.net>2006-05-15 10:59:10 +0000
commit63deb758318c720fa271c458873999466ad2a0c3 (patch)
tree3228a3b237d4ea3e217e53d038e520189119bc93
parentad17714291859a644946586ee953bda7d52a6cee (diff)
downloadcoreutils-63deb758318c720fa271c458873999466ad2a0c3.tar.xz
Avoid the expense of an fstat, when possible.
(O_NOFOLLOW, STREQ): Define. (diropen_fd): Remove function. Merge it into sole caller... (diropen): ...here. Use O_NOFOLLOW when appropriate. (fts_safe_changedir): Call fstat for dev/inode check, only if the previous open/openat call may have opened the wrong directory.
-rw-r--r--lib/fts.c54
1 files changed, 32 insertions, 22 deletions
diff --git a/lib/fts.c b/lib/fts.c
index 5fd01fc3e..2d4e33321 100644
--- a/lib/fts.c
+++ b/lib/fts.c
@@ -132,6 +132,10 @@ static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
#endif
+#if !defined O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+
/* If this host provides the openat function, then we can avoid
attempting to open "." in some initialization code below. */
#ifdef HAVE_OPENAT
@@ -175,6 +179,7 @@ static void free_dir (FTS *fts) {}
#endif
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
+#define STREQ(a, b) (strcmp ((a), (b)) == 0)
#define CLR(opt) (sp->fts_options &= ~(opt))
#define ISSET(opt) (sp->fts_options & (opt))
@@ -213,19 +218,6 @@ bool fts_debug = false;
} \
while (false)
-/* Open the directory DIR if possible, and return a file descriptor.
- As with openat-like functions, if DIR is a relative name,
- interpret it relative to the directory open on file descriptor FD.
- Return -1 and set errno on failure. */
-static int
-internal_function
-diropen_fd (int cwd_fd, char const *dir)
-{
- int fd = openat (cwd_fd, dir,
- O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
- return fd;
-}
-
/* file-descriptor-relative opendir. */
/* FIXME: if others need this function, move it into lib/openat.c */
static inline DIR *
@@ -270,9 +262,12 @@ static inline int
internal_function
diropen (FTS const *sp, char const *dir)
{
- return (ISSET(FTS_CWDFD)
- ? diropen_fd (sp->fts_cwd_fd, dir)
- : open (dir, O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK));
+ int open_flags = (O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
+ | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));
+
+ return (ISSET (FTS_CWDFD)
+ ? openat (sp->fts_cwd_fd, dir, open_flags)
+ : open (dir, open_flags));
}
FTS *
@@ -1432,7 +1427,6 @@ internal_function
fts_safe_changedir (FTS *sp, FTSENT *p, int fd, char const *dir)
{
int ret;
- struct stat sb;
int newfd = fd;
if (ISSET(FTS_NOCHDIR)) {
@@ -1442,16 +1436,32 @@ fts_safe_changedir (FTS *sp, FTSENT *p, int fd, char const *dir)
}
if (fd < 0 && (newfd = diropen (sp, dir)) < 0)
return (-1);
- if (fstat(newfd, &sb)) {
+
+ /* The following dev/inode check is necessary if we're doing
+ a `logical' traversal (through symlinks, a la chown -L),
+ if the system lacks O_NOFOLLOW support, or if we're changing
+ to "..". In the latter case, O_NOFOLLOW can't help. In
+ general (when the target is not ".."), diropen's use of
+ O_NOFOLLOW ensures we don't mistakenly follow a symlink,
+ so we can avoid the expense of this fstat. */
+ if (ISSET(FTS_LOGICAL) || O_NOFOLLOW == 0
+ || (dir && STREQ (dir, "..")))
+ {
+ struct stat sb;
+ if (fstat(newfd, &sb))
+ {
ret = -1;
goto bail;
- }
- if (p->fts_statp->st_dev != sb.st_dev
- || p->fts_statp->st_ino != sb.st_ino) {
+ }
+ if (p->fts_statp->st_dev != sb.st_dev
+ || p->fts_statp->st_ino != sb.st_ino)
+ {
__set_errno (ENOENT); /* disinformation */
ret = -1;
goto bail;
- }
+ }
+ }
+
if (ISSET(FTS_CWDFD))
{
cwd_advance_fd (sp, newfd);