1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <fcntl.h>
4 #include <sys/stat.h>
5 
6 #include "dirent-util.h"
7 #include "path-util.h"
8 #include "stat-util.h"
9 #include "string-util.h"
10 
dirent_ensure_type(DIR * d,struct dirent * de)11 static int dirent_ensure_type(DIR *d, struct dirent *de) {
12         STRUCT_STATX_DEFINE(sx);
13         int r;
14 
15         assert(d);
16         assert(de);
17 
18         if (de->d_type != DT_UNKNOWN)
19                 return 0;
20 
21         if (dot_or_dot_dot(de->d_name)) {
22                 de->d_type = DT_DIR;
23                 return 0;
24         }
25 
26         /* Let's ask only for the type, nothing else. */
27         r = statx_fallback(dirfd(d), de->d_name, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_TYPE, &sx);
28         if (r < 0)
29                 return r;
30 
31         assert(FLAGS_SET(sx.stx_mask, STATX_TYPE));
32         de->d_type = IFTODT(sx.stx_mode);
33 
34         /* If the inode is passed too, update the field, i.e. report most recent data */
35         if (FLAGS_SET(sx.stx_mask, STATX_INO))
36                 de->d_ino = sx.stx_ino;
37 
38         return 0;
39 }
40 
dirent_is_file(const struct dirent * de)41 bool dirent_is_file(const struct dirent *de) {
42         assert(de);
43 
44         if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
45                 return false;
46 
47         if (hidden_or_backup_file(de->d_name))
48                 return false;
49 
50         return true;
51 }
52 
dirent_is_file_with_suffix(const struct dirent * de,const char * suffix)53 bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
54         assert(de);
55 
56         if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
57                 return false;
58 
59         if (de->d_name[0] == '.')
60                 return false;
61 
62         if (!suffix)
63                 return true;
64 
65         return endswith(de->d_name, suffix);
66 }
67 
readdir_ensure_type(DIR * d)68 struct dirent *readdir_ensure_type(DIR *d) {
69         int r;
70 
71         assert(d);
72 
73         /* Like readdir(), but fills in .d_type if it is DT_UNKNOWN */
74 
75         for (;;) {
76                 struct dirent *de;
77 
78                 errno = 0;
79                 de = readdir(d);
80                 if (!de)
81                         return NULL;
82 
83                 r = dirent_ensure_type(d, de);
84                 if (r >= 0)
85                         return de;
86                 if (r != -ENOENT) {
87                         errno = -r; /* We want to be compatible with readdir(), hence propagate error via errno here */
88                         return NULL;
89                 }
90 
91                 /* Vanished by now? Then skip immediately to next */
92         }
93 }
94 
readdir_no_dot(DIR * d)95 struct dirent *readdir_no_dot(DIR *d) {
96         assert(d);
97 
98         for (;;) {
99                 struct dirent *de;
100 
101                 de = readdir_ensure_type(d);
102                 if (!de || !dot_or_dot_dot(de->d_name))
103                         return de;
104         }
105 }
106