1d118b08fSIan Rogers /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2d118b08fSIan Rogers /*
3d118b08fSIan Rogers * Lightweight directory reading library.
4d118b08fSIan Rogers */
5d118b08fSIan Rogers #ifndef __API_IO_DIR__
6d118b08fSIan Rogers #define __API_IO_DIR__
7d118b08fSIan Rogers
8d118b08fSIan Rogers #include <dirent.h>
9d118b08fSIan Rogers #include <fcntl.h>
10d118b08fSIan Rogers #include <stdlib.h>
11d118b08fSIan Rogers #include <unistd.h>
12d118b08fSIan Rogers #include <sys/stat.h>
13d118b08fSIan Rogers #include <sys/syscall.h>
14*0c9f3a85SArnaldo Carvalho de Melo #include <linux/limits.h>
15d118b08fSIan Rogers
16d118b08fSIan Rogers #if !defined(SYS_getdents64)
17d118b08fSIan Rogers #if defined(__x86_64__) || defined(__arm__)
18d118b08fSIan Rogers #define SYS_getdents64 217
19d118b08fSIan Rogers #elif defined(__i386__) || defined(__s390x__) || defined(__sh__)
20d118b08fSIan Rogers #define SYS_getdents64 220
21d118b08fSIan Rogers #elif defined(__alpha__)
22d118b08fSIan Rogers #define SYS_getdents64 377
23d118b08fSIan Rogers #elif defined(__mips__)
24d118b08fSIan Rogers #define SYS_getdents64 308
25d118b08fSIan Rogers #elif defined(__powerpc64__) || defined(__powerpc__)
26d118b08fSIan Rogers #define SYS_getdents64 202
27d118b08fSIan Rogers #elif defined(__sparc64__) || defined(__sparc__)
28d118b08fSIan Rogers #define SYS_getdents64 154
29d118b08fSIan Rogers #elif defined(__xtensa__)
30d118b08fSIan Rogers #define SYS_getdents64 60
31d118b08fSIan Rogers #else
32d118b08fSIan Rogers #define SYS_getdents64 61
33d118b08fSIan Rogers #endif
34d118b08fSIan Rogers #endif /* !defined(SYS_getdents64) */
35d118b08fSIan Rogers
perf_getdents64(int fd,void * dirp,size_t count)36d118b08fSIan Rogers static inline ssize_t perf_getdents64(int fd, void *dirp, size_t count)
37d118b08fSIan Rogers {
38d118b08fSIan Rogers #ifdef MEMORY_SANITIZER
39d118b08fSIan Rogers memset(dirp, 0, count);
40d118b08fSIan Rogers #endif
41d118b08fSIan Rogers return syscall(SYS_getdents64, fd, dirp, count);
42d118b08fSIan Rogers }
43d118b08fSIan Rogers
44d118b08fSIan Rogers struct io_dirent64 {
45d118b08fSIan Rogers ino64_t d_ino; /* 64-bit inode number */
46d118b08fSIan Rogers off64_t d_off; /* 64-bit offset to next structure */
47d118b08fSIan Rogers unsigned short d_reclen; /* Size of this dirent */
48d118b08fSIan Rogers unsigned char d_type; /* File type */
49d118b08fSIan Rogers char d_name[NAME_MAX + 1]; /* Filename (null-terminated) */
50d118b08fSIan Rogers };
51d118b08fSIan Rogers
52d118b08fSIan Rogers struct io_dir {
53d118b08fSIan Rogers int dirfd;
54d118b08fSIan Rogers ssize_t available_bytes;
55d118b08fSIan Rogers struct io_dirent64 *next;
56d118b08fSIan Rogers struct io_dirent64 buff[4];
57d118b08fSIan Rogers };
58d118b08fSIan Rogers
io_dir__init(struct io_dir * iod,int dirfd)59d118b08fSIan Rogers static inline void io_dir__init(struct io_dir *iod, int dirfd)
60d118b08fSIan Rogers {
61d118b08fSIan Rogers iod->dirfd = dirfd;
62d118b08fSIan Rogers iod->available_bytes = 0;
63d118b08fSIan Rogers }
64d118b08fSIan Rogers
io_dir__rewinddir(struct io_dir * iod)65d118b08fSIan Rogers static inline void io_dir__rewinddir(struct io_dir *iod)
66d118b08fSIan Rogers {
67d118b08fSIan Rogers lseek(iod->dirfd, 0, SEEK_SET);
68d118b08fSIan Rogers iod->available_bytes = 0;
69d118b08fSIan Rogers }
70d118b08fSIan Rogers
io_dir__readdir(struct io_dir * iod)71d118b08fSIan Rogers static inline struct io_dirent64 *io_dir__readdir(struct io_dir *iod)
72d118b08fSIan Rogers {
73d118b08fSIan Rogers struct io_dirent64 *entry;
74d118b08fSIan Rogers
75d118b08fSIan Rogers if (iod->available_bytes <= 0) {
76d118b08fSIan Rogers ssize_t rc = perf_getdents64(iod->dirfd, iod->buff, sizeof(iod->buff));
77d118b08fSIan Rogers
78d118b08fSIan Rogers if (rc <= 0)
79d118b08fSIan Rogers return NULL;
80d118b08fSIan Rogers iod->available_bytes = rc;
81d118b08fSIan Rogers iod->next = iod->buff;
82d118b08fSIan Rogers }
83d118b08fSIan Rogers entry = iod->next;
84d118b08fSIan Rogers iod->next = (struct io_dirent64 *)((char *)entry + entry->d_reclen);
85d118b08fSIan Rogers iod->available_bytes -= entry->d_reclen;
86d118b08fSIan Rogers return entry;
87d118b08fSIan Rogers }
88d118b08fSIan Rogers
io_dir__is_dir(const struct io_dir * iod,struct io_dirent64 * dent)89d118b08fSIan Rogers static inline bool io_dir__is_dir(const struct io_dir *iod, struct io_dirent64 *dent)
90d118b08fSIan Rogers {
91d118b08fSIan Rogers if (dent->d_type == DT_UNKNOWN) {
92d118b08fSIan Rogers struct stat st;
93d118b08fSIan Rogers
94d118b08fSIan Rogers if (fstatat(iod->dirfd, dent->d_name, &st, /*flags=*/0))
95d118b08fSIan Rogers return false;
96d118b08fSIan Rogers
97d118b08fSIan Rogers if (S_ISDIR(st.st_mode)) {
98d118b08fSIan Rogers dent->d_type = DT_DIR;
99d118b08fSIan Rogers return true;
100d118b08fSIan Rogers }
101d118b08fSIan Rogers }
102d118b08fSIan Rogers return dent->d_type == DT_DIR;
103d118b08fSIan Rogers }
104d118b08fSIan Rogers
105d118b08fSIan Rogers #endif /* __API_IO_DIR__ */
106