/* Traverse a file hierarchy.
- Copyright (C) 2004-2009 Free Software Foundation, Inc.
+ Copyright (C) 2004-2011 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
# define fchdir __fchdir
# undef open
# define open __open
-# undef opendir
-# define opendir __opendir
# undef readdir
# define readdir __readdir
#else
static int fts_safe_changedir (FTS *, FTSENT *, int, const char *)
internal_function;
-#if GNULIB_FTS
-# include "fts-cycle.c"
-#else
-static bool enter_dir (FTS *fts, FTSENT *ent) { return true; }
-static void leave_dir (FTS *fts, FTSENT *ent) {}
-static bool setup_dir (FTS *fts) { return true; }
-static void free_dir (FTS *fts) {}
-#endif
+#include "fts-cycle.c"
#ifndef MAX
# define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
-#define STREQ(a, b) (strcmp ((a), (b)) == 0)
+#define STREQ(a, b) (strcmp (a, b) == 0)
#define CLR(opt) (sp->fts_options &= ~(opt))
#define ISSET(opt) (sp->fts_options & (opt))
/* FIXME: if others need this function, move it into lib/openat.c */
static inline DIR *
internal_function
-opendirat (int fd, char const *dir)
+opendirat (int fd, char const *dir, int extra_flags, int *pdir_fd)
{
int new_fd = openat (fd, dir,
- O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
+ (O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
+ | extra_flags));
DIR *dirp;
if (new_fd < 0)
return NULL;
set_cloexec_flag (new_fd, true);
dirp = fdopendir (new_fd);
- if (dirp == NULL)
+ if (dirp)
+ *pdir_fd = new_fd;
+ else
{
int saved_errno = errno;
close (new_fd);
internal_function
diropen (FTS const *sp, char const *dir)
{
- int open_flags = (O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
- | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));
+ int open_flags = (O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
+ | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0)
+ | (ISSET (FTS_NOATIME) ? O_NOATIME : 0));
int fd = (ISSET (FTS_CWDFD)
? openat (sp->fts_cwd_fd, dir, open_flags)
early, doing it here saves us the trouble of ensuring
later (where it'd be messier) that "." can in fact
be opened. If not, revert to FTS_NOCHDIR mode. */
- int fd = open (".", O_RDONLY);
+ int fd = open (".",
+ O_SEARCH | (ISSET (FTS_NOATIME) ? O_NOATIME : 0));
if (fd < 0)
{
/* Even if `.' is unreadable, don't revert to FTS_NOCHDIR mode
fd_ring_clear (&sp->fts_fd_ring);
-#if GNULIB_FTS
if (sp->fts_leaf_optimization_works_ht)
hash_free (sp->fts_leaf_optimization_works_ht);
-#endif
free_dir (sp);
#if defined __linux__ \
&& HAVE_SYS_VFS_H && HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE
-#include <sys/vfs.h>
+# include <sys/vfs.h>
/* Linux-specific constants from coreutils' src/fs.h */
# define S_MAGIC_TMPFS 0x1021994
leaf_optimization_applies (int dir_fd _GL_UNUSED) { return false; }
#endif
-#if GNULIB_FTS
/* link-count-optimization entry:
- map an stat.st_dev number to a boolean: leaf_optimization_works */
+ map a stat.st_dev number to a boolean: leaf_optimization_works */
struct LCO_ent
{
dev_t st_dev;
return opt_ok;
}
-#endif
/*
* Special case of "/" at the end of the file name so that slashes aren't
st->st_mode = type;
}
+#define closedir_and_clear(dirp) \
+ do \
+ { \
+ closedir (dirp); \
+ dirp = NULL; \
+ } \
+ while (0)
+
+#define fts_opendir(file, Pdir_fd) \
+ opendirat((! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD) \
+ ? sp->fts_cwd_fd : AT_FDCWD), \
+ file, \
+ (((ISSET(FTS_PHYSICAL) \
+ && ! (ISSET(FTS_COMFOLLOW) \
+ && cur->fts_level == FTS_ROOTLEVEL)) \
+ ? O_NOFOLLOW : 0) \
+ | (ISSET (FTS_NOATIME) ? O_NOATIME : 0)), \
+ Pdir_fd)
+
/*
* This is the tricky part -- do not casually change *anything* in here. The
* idea is to build the linked list of entries that are used by fts_children
register struct dirent *dp;
register FTSENT *p, *head;
register size_t nitems;
- FTSENT *cur, *tail;
- DIR *dirp;
+ FTSENT *tail;
void *oldaddr;
int saved_errno;
bool descend;
bool nostat;
size_t len, maxlen, new_len;
char *cp;
+ int dir_fd;
+ FTSENT *cur = sp->fts_cur;
- /* Set current node pointer. */
- cur = sp->fts_cur;
-
- /*
- * Open the directory for reading. If this fails, we're done.
- * If being called from fts_read, set the fts_info field.
- */
-#if defined FTS_WHITEOUT && 0
- if (ISSET(FTS_WHITEOUT))
- oflag = DTF_NODUP|DTF_REWIND;
- else
- oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
-#else
-# define __opendir2(file, flag) \
- ( ! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD) \
- ? opendirat(sp->fts_cwd_fd, file) \
- : opendir(file))
-#endif
- if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
+ /* Open the directory for reading. If this fails, we're done.
+ If being called from fts_read, set the fts_info field. */
+ if ((cur->fts_dirp = fts_opendir(cur->fts_accpath, &dir_fd)) == NULL) {
if (type == BREAD) {
cur->fts_info = FTS_DNR;
cur->fts_errno = errno;
}
return (NULL);
}
- /* Rather than calling fts_stat for each and every entry encountered
- in the readdir loop (below), stat each directory only right after
- opening it. */
- if (cur->fts_info == FTS_NSOK)
- cur->fts_info = fts_stat(sp, cur, false);
- else if (sp->fts_options & FTS_TIGHT_CYCLE_CHECK) {
- /* Now read the stat info again after opening a directory to
- * reveal eventual changes caused by a submount triggered by
- * the traversal. But do it only for utilities which use
- * FTS_TIGHT_CYCLE_CHECK. Therefore, only find and du
- * benefit/suffer from this feature for now.
- */
- LEAVE_DIR (sp, cur, "4");
- fts_stat (sp, cur, false);
- if (! enter_dir (sp, cur)) {
- __set_errno (ENOMEM);
- return NULL;
- }
- }
+ /* Rather than calling fts_stat for each and every entry encountered
+ in the readdir loop (below), stat each directory only right after
+ opening it. */
+ if (cur->fts_info == FTS_NSOK)
+ cur->fts_info = fts_stat(sp, cur, false);
+ else if (sp->fts_options & FTS_TIGHT_CYCLE_CHECK)
+ {
+ /* Now read the stat info again after opening a directory to
+ reveal eventual changes caused by a submount triggered by
+ the traversal. But do it only for utilities which use
+ FTS_TIGHT_CYCLE_CHECK. Therefore, only find and du
+ benefit/suffer from this feature for now. */
+ LEAVE_DIR (sp, cur, "4");
+ fts_stat (sp, cur, false);
+ if (! enter_dir (sp, cur))
+ {
+ __set_errno (ENOMEM);
+ return NULL;
+ }
+ }
- /*
- * Nlinks is the number of possible entries of type directory in the
- * directory if we're cheating on stat calls, 0 if we're not doing
- * any stat calls at all, (nlink_t) -1 if we're statting everything.
- */
+ /* Nlinks is the number of possible entries of type directory in the
+ directory if we're cheating on stat calls, 0 if we're not doing
+ any stat calls at all, (nlink_t) -1 if we're statting everything. */
if (type == BNAMES) {
nlinks = 0;
/* Be quiet about nostat, GCC. */
* checking FTS_NS on the returned nodes.
*/
if (nlinks || type == BREAD) {
- int dir_fd = dirfd(dirp);
- if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
+ if (ISSET(FTS_CWDFD))
{
dir_fd = dup (dir_fd);
- set_cloexec_flag (dir_fd, true);
+ if (0 <= dir_fd)
+ set_cloexec_flag (dir_fd, true);
}
if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {
if (nlinks && type == BREAD)
cur->fts_errno = errno;
cur->fts_flags |= FTS_DONTCHDIR;
descend = false;
- closedir(dirp);
+ closedir_and_clear(cur->fts_dirp);
if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
close (dir_fd);
- dirp = NULL;
+ cur->fts_dirp = NULL;
} else
descend = true;
} else
/* Read the directory, attaching each entry to the `link' pointer. */
doadjust = false;
- for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) {
+ for (head = tail = NULL, nitems = 0; cur->fts_dirp && (dp = readdir(cur->fts_dirp));) {
bool is_dir;
if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
mem1: saved_errno = errno;
free(p);
fts_lfree(head);
- closedir(dirp);
+ closedir_and_clear(cur->fts_dirp);
cur->fts_info = FTS_ERR;
SET(FTS_STOP);
__set_errno (saved_errno);
*/
free(p);
fts_lfree(head);
- closedir(dirp);
+ closedir_and_clear(cur->fts_dirp);
cur->fts_info = FTS_ERR;
SET(FTS_STOP);
__set_errno (ENAMETOOLONG);
p->fts_parent = sp->fts_cur;
p->fts_pathlen = new_len;
-#if defined FTS_WHITEOUT && 0
- if (dp->d_type == DT_WHT)
- p->fts_flags |= FTS_ISW;
-#endif
/* Store dirent.d_ino, in case we need to sort
entries before processing them. */
p->fts_statp->st_ino = D_INO (dp);
}
++nitems;
}
- if (dirp)
- closedir(dirp);
+ if (cur->fts_dirp)
+ closedir_and_clear(cur->fts_dirp);
/*
* If realloc() changed the address of the file name, adjust the
int fd = i_ring_pop (&fd_w);
if (0 <= fd)
{
- int parent_fd = openat (cwd_fd, "..", O_RDONLY);
+ int parent_fd = openat (cwd_fd, "..", O_SEARCH | O_NOATIME);
if (parent_fd < 0)
{
// Warn?
if (p->fts_level == FTS_ROOTLEVEL && ISSET(FTS_COMFOLLOW))
follow = true;
-#if defined FTS_WHITEOUT && 0
- /* check for whiteout */
- if (p->fts_flags & FTS_ISW) {
- memset(sbp, '\0', sizeof (*sbp));
- sbp->st_mode = S_IFWHT;
- return (FTS_W);
- }
-#endif
-
/*
* If doing a logical walk, or application requested FTS_FOLLOW, do
* a stat(2). If that fails, check for a non-existent symlink. If
return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT);
}
-#if !GNULIB_FTS
- {
- /*
- * Cycle detection is done by brute force when the directory
- * is first encountered. If the tree gets deep enough or the
- * number of symbolic links to directories is high enough,
- * something faster might be worthwhile.
- */
- FTSENT *t;
-
- for (t = p->fts_parent;
- t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
- if (sbp->st_ino == t->fts_statp->st_ino
- && sbp->st_dev == t->fts_statp->st_dev)
- {
- p->fts_cycle = t;
- return (FTS_DC);
- }
- }
-#endif
-
return (FTS_D);
}
if (S_ISLNK(sbp->st_mode))
p->fts_fts = sp;
p->fts_path = sp->fts_path;
p->fts_errno = 0;
+ p->fts_dirp = NULL;
p->fts_flags = 0;
p->fts_instr = FTS_NOINSTR;
p->fts_number = 0;
/* Free a linked list of structures. */
while ((p = head)) {
head = head->fts_link;
+ if (p->fts_dirp)
+ closedir (p->fts_dirp);
free(p);
}
}