/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include <fcntl.h> #include <sys/stat.h> #include "dirent-util.h" #include "path-util.h" #include "stat-util.h" #include "string-util.h" int dirent_ensure_type(int dir_fd, struct dirent *de) { STRUCT_STATX_DEFINE(sx); int r; assert(dir_fd >= 0); assert(de); if (de->d_type != DT_UNKNOWN) return 0; if (dot_or_dot_dot(de->d_name)) { de->d_type = DT_DIR; return 0; } /* Let's ask only for the type, nothing else. */ r = statx_fallback(dir_fd, de->d_name, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_TYPE, &sx); if (r < 0) return r; 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; } bool dirent_is_file(const struct dirent *de) { assert(de); if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) return false; if (hidden_or_backup_file(de->d_name)) return false; return true; } bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { assert(de); if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) return false; if (de->d_name[0] == '.') return false; if (!suffix) return true; return endswith(de->d_name, suffix); } struct dirent *readdir_ensure_type(DIR *d) { int r; assert(d); /* Like readdir(), but fills in .d_type if it is DT_UNKNOWN */ for (;;) { struct dirent *de; errno = 0; de = readdir(d); if (!de) return NULL; r = dirent_ensure_type(dirfd(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 immediately 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; } }