summaryrefslogtreecommitdiff
path: root/io/ftw.c
diff options
context:
space:
mode:
Diffstat (limited to 'io/ftw.c')
-rw-r--r--io/ftw.c62
1 files changed, 47 insertions, 15 deletions
diff --git a/io/ftw.c b/io/ftw.c
index f24a6b46fe..50303d9cd0 100644
--- a/io/ftw.c
+++ b/io/ftw.c
@@ -66,6 +66,7 @@ char *alloca ();
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <not-cancel.h>
#if HAVE_SYS_PARAM_H || defined _LIBC
# include <sys/param.h>
#endif
@@ -143,9 +144,11 @@ int rpl_lstat (const char *, struct stat *);
# ifdef _LIBC
# define LXSTAT __lxstat
# define XSTAT __xstat
+# define FXSTATAT __fxstatat
# else
# define LXSTAT(V,f,sb) lstat (f,sb)
# define XSTAT(V,f,sb) stat (f,sb)
+# define FXSTATAT(V,d,f,sb,m) fstatat (d, f, sb, m)
# endif
# define FTW_FUNC_T __ftw_func_t
# define NFTW_FUNC_T __nftw_func_t
@@ -162,6 +165,7 @@ int rpl_lstat (const char *, struct stat *);
struct dir_data
{
DIR *stream;
+ int streamfd;
char *content;
};
@@ -263,7 +267,7 @@ find_object (struct ftw_data *data, struct STAT *st)
static inline int
__attribute ((always_inline))
-open_dir_stream (struct ftw_data *data, struct dir_data *dirp)
+open_dir_stream (int *dfdp, struct ftw_data *data, struct dir_data *dirp)
{
int result = 0;
@@ -324,6 +328,7 @@ open_dir_stream (struct ftw_data *data, struct dir_data *dirp)
{
__closedir (st);
data->dirstreams[data->actdir]->stream = NULL;
+ data->dirstreams[data->actdir]->streamfd = -1;
data->dirstreams[data->actdir] = NULL;
}
}
@@ -332,15 +337,28 @@ open_dir_stream (struct ftw_data *data, struct dir_data *dirp)
/* Open the new stream. */
if (result == 0)
{
- const char *name = ((data->flags & FTW_CHDIR)
- ? data->dirbuf + data->ftw.base: data->dirbuf);
assert (data->dirstreams[data->actdir] == NULL);
- dirp->stream = __opendir (name);
+ if (dfdp != NULL && *dfdp != -1)
+ {
+ int fd = openat64_not_cancel_3 (*dfdp, data->dirbuf + data->ftw.base,
+ O_RDONLY | O_DIRECTORY | O_NDELAY);
+ dirp->stream = NULL;
+ if (fd != -1 && (dirp->stream = __fdopendir (fd)) == NULL)
+ close_not_cancel_no_status (fd);
+ }
+ else
+ {
+ const char *name = ((data->flags & FTW_CHDIR)
+ ? data->dirbuf + data->ftw.base: data->dirbuf);
+ dirp->stream = __opendir (name);
+ }
+
if (dirp->stream == NULL)
result = -1;
else
{
+ dirp->streamfd = dirfd (dirp->stream);
dirp->content = NULL;
data->dirstreams[data->actdir] = dirp;
@@ -356,7 +374,7 @@ open_dir_stream (struct ftw_data *data, struct dir_data *dirp)
static int
internal_function
process_entry (struct ftw_data *data, struct dir_data *dir, const char *name,
- size_t namlen)
+ size_t namlen, int d_type)
{
struct STAT st;
int result = 0;
@@ -383,18 +401,28 @@ process_entry (struct ftw_data *data, struct dir_data *dir, const char *name,
*((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0';
- if ((data->flags & FTW_CHDIR) == 0)
- name = data->dirbuf;
+ int statres;
+ if (dir->streamfd != -1)
+ statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st,
+ (data->flags & FTW_PHYS) ? AT_SYMLINK_NOFOLLOW : 0);
+ else
+ {
+ if ((data->flags & FTW_CHDIR) == 0)
+ name = data->dirbuf;
+
+ statres = ((data->flags & FTW_PHYS)
+ ? LXSTAT (_STAT_VER, name, &st)
+ : XSTAT (_STAT_VER, name, &st));
+ }
- if (((data->flags & FTW_PHYS)
- ? LXSTAT (_STAT_VER, name, &st)
- : XSTAT (_STAT_VER, name, &st)) < 0)
+ if (statres < 0)
{
if (errno != EACCES && errno != ENOENT)
result = -1;
else if (!(data->flags & FTW_PHYS)
- && LXSTAT (_STAT_VER, name, &st) == 0
- && S_ISLNK (st.st_mode))
+ && (d_type == DT_LNK
+ || (LXSTAT (_STAT_VER, name, &st) == 0
+ && S_ISLNK (st.st_mode))))
flag = FTW_SLN;
else
flag = FTW_NS;
@@ -446,7 +474,8 @@ ftw_dir (struct ftw_data *data, struct STAT *st, struct dir_data *old_dir)
/* Open the stream for this directory. This might require that
another stream has to be closed. */
- result = open_dir_stream (data, &dir);
+ result = open_dir_stream (old_dir == NULL ? NULL : &old_dir->streamfd,
+ data, &dir);
if (result != 0)
{
if (errno == EACCES)
@@ -466,6 +495,7 @@ ftw_dir (struct ftw_data *data, struct STAT *st, struct dir_data *old_dir)
fail:
save_err = errno;
__closedir (dir.stream);
+ dir.streamfd = -1;
__set_errno (save_err);
if (data->actdir-- == 0)
@@ -496,7 +526,7 @@ fail:
while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL)
{
- result = process_entry (data, &dir, d->d_name, NAMLEN (d));
+ result = process_entry (data, &dir, d->d_name, NAMLEN (d), d->d_type);
if (result != 0)
break;
}
@@ -510,6 +540,7 @@ fail:
assert (dir.content == NULL);
__closedir (dir.stream);
+ dir.streamfd = -1;
__set_errno (save_err);
if (data->actdir-- == 0)
@@ -525,7 +556,8 @@ fail:
{
char *endp = strchr (runp, '\0');
- result = process_entry (data, &dir, runp, endp - runp);
+ // XXX Should store the d_type values as well?!
+ result = process_entry (data, &dir, runp, endp - runp, DT_UNKNOWN);
runp = endp + 1;
}