| /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ |
| /* |
| * Lightweight directory reading library. |
| */ |
| #ifndef __API_IO_DIR__ |
| #define __API_IO_DIR__ |
| |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <sys/stat.h> |
| #include <sys/syscall.h> |
| #include <linux/limits.h> |
| |
| #if !defined(SYS_getdents64) |
| #if defined(__x86_64__) || defined(__arm__) |
| #define SYS_getdents64 217 |
| #elif defined(__i386__) || defined(__s390x__) || defined(__sh__) |
| #define SYS_getdents64 220 |
| #elif defined(__alpha__) |
| #define SYS_getdents64 377 |
| #elif defined(__mips__) |
| #define SYS_getdents64 308 |
| #elif defined(__powerpc64__) || defined(__powerpc__) |
| #define SYS_getdents64 202 |
| #elif defined(__sparc64__) || defined(__sparc__) |
| #define SYS_getdents64 154 |
| #elif defined(__xtensa__) |
| #define SYS_getdents64 60 |
| #else |
| #define SYS_getdents64 61 |
| #endif |
| #endif /* !defined(SYS_getdents64) */ |
| |
| static inline ssize_t perf_getdents64(int fd, void *dirp, size_t count) |
| { |
| #ifdef MEMORY_SANITIZER |
| memset(dirp, 0, count); |
| #endif |
| return syscall(SYS_getdents64, fd, dirp, count); |
| } |
| |
| struct io_dirent64 { |
| ino64_t d_ino; /* 64-bit inode number */ |
| off64_t d_off; /* 64-bit offset to next structure */ |
| unsigned short d_reclen; /* Size of this dirent */ |
| unsigned char d_type; /* File type */ |
| char d_name[NAME_MAX + 1]; /* Filename (null-terminated) */ |
| }; |
| |
| struct io_dir { |
| int dirfd; |
| ssize_t available_bytes; |
| struct io_dirent64 *next; |
| struct io_dirent64 buff[4]; |
| }; |
| |
| static inline void io_dir__init(struct io_dir *iod, int dirfd) |
| { |
| iod->dirfd = dirfd; |
| iod->available_bytes = 0; |
| } |
| |
| static inline void io_dir__rewinddir(struct io_dir *iod) |
| { |
| lseek(iod->dirfd, 0, SEEK_SET); |
| iod->available_bytes = 0; |
| } |
| |
| static inline struct io_dirent64 *io_dir__readdir(struct io_dir *iod) |
| { |
| struct io_dirent64 *entry; |
| |
| if (iod->available_bytes <= 0) { |
| ssize_t rc = perf_getdents64(iod->dirfd, iod->buff, sizeof(iod->buff)); |
| |
| if (rc <= 0) |
| return NULL; |
| iod->available_bytes = rc; |
| iod->next = iod->buff; |
| } |
| entry = iod->next; |
| iod->next = (struct io_dirent64 *)((char *)entry + entry->d_reclen); |
| iod->available_bytes -= entry->d_reclen; |
| return entry; |
| } |
| |
| static inline bool io_dir__is_dir(const struct io_dir *iod, struct io_dirent64 *dent) |
| { |
| if (dent->d_type == DT_UNKNOWN) { |
| struct stat st; |
| |
| if (fstatat(iod->dirfd, dent->d_name, &st, /*flags=*/0)) |
| return false; |
| |
| if (S_ISDIR(st.st_mode)) { |
| dent->d_type = DT_DIR; |
| return true; |
| } |
| } |
| return dent->d_type == DT_DIR; |
| } |
| |
| #endif /* __API_IO_DIR__ */ |