diff options
author | Jim Meyering <jim@meyering.net> | 2006-05-15 10:59:10 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 2006-05-15 10:59:10 +0000 |
commit | 63deb758318c720fa271c458873999466ad2a0c3 (patch) | |
tree | 3228a3b237d4ea3e217e53d038e520189119bc93 | |
parent | ad17714291859a644946586ee953bda7d52a6cee (diff) | |
download | coreutils-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.c | 54 |
1 files changed, 32 insertions, 22 deletions
@@ -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); |