summaryrefslogtreecommitdiffstats
path: root/src/basic/dirent-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic/dirent-util.c')
-rw-r--r--src/basic/dirent-util.c105
1 files changed, 105 insertions, 0 deletions
diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c
new file mode 100644
index 0000000..2eea228
--- /dev/null
+++ b/src/basic/dirent-util.c
@@ -0,0 +1,105 @@
+/* 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"
+
+static int dirent_ensure_type(DIR *d, struct dirent *de) {
+ STRUCT_STATX_DEFINE(sx);
+ int r;
+
+ assert(d);
+ 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(dirfd(d), 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(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;
+ }
+}