diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c index a70871b33d..d578e6836d 100644 --- a/src/basic/dirent-util.c +++ b/src/basic/dirent-util.c @@ -5,22 +5,12 @@ #include "dirent-util.h" #include "path-util.h" +#include "stat-util.h" #include "string-util.h" -int stat_mode_to_dirent_type(mode_t mode) { - return - S_ISREG(mode) ? DT_REG : - S_ISDIR(mode) ? DT_DIR : - S_ISLNK(mode) ? DT_LNK : - S_ISFIFO(mode) ? DT_FIFO : - S_ISSOCK(mode) ? DT_SOCK : - S_ISCHR(mode) ? DT_CHR : - S_ISBLK(mode) ? DT_BLK : - DT_UNKNOWN; -} - static int dirent_ensure_type(DIR *d, struct dirent *de) { - struct stat st; + STRUCT_STATX_DEFINE(sx); + int r; assert(d); assert(de); @@ -33,10 +23,17 @@ static int dirent_ensure_type(DIR *d, struct dirent *de) { return 0; } - if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) - return -errno; + /* Let's ask only for the type, nothing else. */ + r = statx_fallback(dirfd(d), de->d_name, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_TYPE, &sx); + if (r < 0) + return r; - de->d_type = stat_mode_to_dirent_type(st.st_mode); + assert(FLAGS_SET(sx.stx_mask, STATX_TYPE)); + de->d_type = IFTODT(sx.stx_mode); + + /* If the inode is passed too, update the field, i.e. report most recent data */ + if (FLAGS_SET(sx.stx_mask, STATX_INO)) + de->d_ino = sx.stx_ino; return 0; } @@ -69,24 +66,40 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { } struct dirent *readdir_ensure_type(DIR *d) { - struct dirent *de; + int r; assert(d); - errno = 0; - de = readdir(d); - if (de) - (void) dirent_ensure_type(d, de); - return de; -} - -struct dirent *readdir_no_dot(DIR *dirp) { - struct dirent *d; + /* Like readdir(), but fills in .d_type if it is DT_UNKNOWN */ for (;;) { - d = readdir_ensure_type(dirp); - if (d && dot_or_dot_dot(d->d_name)) - continue; - return d; + struct dirent *de; + + errno = 0; + de = readdir(d); + if (!de) + return NULL; + + r = dirent_ensure_type(d, de); + if (r >= 0) + return de; + if (r != -ENOENT) { + errno = -r; /* We want to be compatible with readdir(), hence propagate error via errno here */ + return NULL; + } + + /* Vanished by now? Then skip immedately to next */ + } +} + +struct dirent *readdir_no_dot(DIR *d) { + assert(d); + + for (;;) { + struct dirent *de; + + de = readdir_ensure_type(d); + if (!de || !dot_or_dot_dot(de->d_name)) + return de; } } diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h index 5bbd54df5a..c7956e7c1b 100644 --- a/src/basic/dirent-util.h +++ b/src/basic/dirent-util.h @@ -8,8 +8,6 @@ #include "macro.h" #include "path-util.h" -int stat_mode_to_dirent_type(mode_t mode); - bool dirent_is_file(const struct dirent *de) _pure_; bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_; diff --git a/src/basic/recurse-dir.c b/src/basic/recurse-dir.c index fe34ffbfd4..2aabbccadb 100644 --- a/src/basic/recurse-dir.c +++ b/src/basic/recurse-dir.c @@ -290,7 +290,7 @@ int recurse_dir( /* Copy over the data we acquired through statx() if we acquired any */ if (sx.stx_mask & STATX_TYPE) { assert(!!subdir == !!S_ISDIR(sx.stx_mode)); - de[i]->d_type = stat_mode_to_dirent_type(sx.stx_mode); + de[i]->d_type = IFTODT(sx.stx_mode); } if (sx.stx_mask & STATX_INO)