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