1 // Copyright (C) 2025 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 #ifndef _AEMU_DIRENT_H_ 15 #define _AEMU_DIRENT_H_ 16 17 #include <sys/types.h> 18 #include <windows.h> 19 20 #ifdef __cplusplus 21 extern "C" { 22 #endif 23 24 /** 25 * @file dirent.h 26 * @brief A POSIX-like dirent API implementation for Windows using the Windows API. 27 * 28 * This header provides a subset of the POSIX dirent API for Windows, allowing C and C++ 29 * code to use familiar functions like opendir(), readdir(), closedir(), etc. to 30 * iterate through directory entries. 31 * 32 * @warning **Limitations:** 33 * - **`telldir()` and `seekdir()` are minimally implemented.** `seekdir()` only supports 34 * seeking to the beginning (loc = 0), the end (loc = -1), or forward to a specific entry 35 * by its index (loc > 0). Seeking to arbitrary positions is implemented by iterating 36 * through the entries, making it an **O(N)** operation in the worst case, where N is 37 * the desired position. `telldir()` returns the index of the last entry read by `readdir()`. 38 * - **`d_ino` is implemented using Windows file index.** It does not represent a 39 * true POSIX inode number but can be used to identify files uniquely. 40 * - **`d_reclen` is not supported.** The field is not present in this implementation. 41 * - **Thread safety:** This implementation is not inherently thread-safe. Using the 42 * same `DIR` pointer from multiple threads simultaneously can lead to undefined 43 * behavior. 44 * 45 * @note **Windows-Specific Behavior:** 46 * - Filenames are stored in `d_name` as **UTF-8** encoded strings. 47 * - Extended-length paths (longer than `MAX_PATH`) are supported using the `\\?\` prefix. 48 * - The implementation uses the Windows API (`FindFirstFileW`, `FindNextFileW`, etc.) 49 * internally. 50 * - The `DIR` type is an opaque pointer to an internal structure. 51 */ 52 53 /** 54 * @brief The maximum length of a file name, including the null terminator. 55 * 56 * This is set to `MAX_PATH` (260) for compatibility but internally the implementation 57 * supports extended-length paths using the `\\?\` prefix. 58 */ 59 #define FILENAME_MAX MAX_PATH 60 61 /** 62 * @brief Represents a directory entry. 63 */ 64 struct dirent { 65 /** 66 * @brief File ID (from the Windows file index). 67 * 68 * This is not a true POSIX inode number but can be used as a unique file 69 * identifier on Windows. It is obtained using `GetFileInformationByHandle` 70 * and represents a file's unique ID within a volume. 71 * @warning This field might not be fully unique across different volumes or over time. 72 */ 73 uint64_t d_ino; 74 75 /** 76 * @brief Null-terminated file name in UTF-8 encoding. 77 * 78 * @warning The maximum length of the filename (excluding the null terminator) 79 * that can be stored in this field is `FILENAME_MAX`. If a filename exceeds this 80 * limit, `readdir` will skip the entry and set `errno` to `ENAMETOOLONG`. 81 */ 82 char d_name[FILENAME_MAX]; 83 }; 84 85 /** 86 * @brief An opaque type representing a directory stream. 87 */ 88 typedef struct DIR DIR; 89 90 91 /** 92 * @brief Opens a directory stream for reading. 93 * 94 * @param name The path to the directory to open. This should be a UTF-8 encoded string. 95 * 96 * @return A pointer to a `DIR` structure representing the opened directory stream, 97 * or `nullptr` if an error occurred. If `nullptr` is returned, `errno` is set 98 * to indicate the error. 99 * 100 * @retval EACCES Search permission is denied for the directory. 101 * @retval EMFILE The maximum number of file descriptors are already open. 102 * @retval ENFILE The maximum number of files are already open in the system. 103 * @retval ENOENT The named directory does not exist or is an empty string. 104 * @retval ENOMEM Insufficient memory is available. 105 * @retval ENOTDIR A component of the path is not a directory. 106 * @retval EINVAL The `name` argument is invalid (e.g., contains invalid characters). 107 */ 108 DIR* opendir(const char* name); 109 110 /** 111 * @brief Reads the next directory entry from a directory stream. 112 * 113 * @param dirp A pointer to a `DIR` structure returned by `opendir()`. 114 * 115 * @return A pointer to a `dirent` structure representing the next directory entry, 116 * or `nullptr` if the end of the directory stream is reached or an error 117 * occurred. If `nullptr` is returned and `errno` is not 0, an error occurred. 118 * 119 * @retval EBADF The `dirp` argument does not refer to an open directory stream. 120 * @retval ENOMEM Insufficient memory is available. 121 * @retval ENOENT No more directory entries. 122 * @retval EIO An I/O error occurred. 123 * @retval ENAMETOOLONG A filename exceeded `FILENAME_MAX`. 124 */ 125 struct dirent* readdir(DIR* dirp); 126 127 /** 128 * @brief Closes a directory stream. 129 * 130 * @param dirp A pointer to a `DIR` structure returned by `opendir()`. 131 * 132 * @return 0 on success, -1 on failure. If -1 is returned, `errno` is set to 133 * indicate the error. 134 * 135 * @retval EBADF The `dirp` argument does not refer to an open directory stream. 136 */ 137 int closedir(DIR* dirp); 138 139 /** 140 * @brief Resets the position of a directory stream to the beginning. 141 * 142 * @param dirp A pointer to a `DIR` structure returned by `opendir()`. 143 * 144 * @retval EBADF The `dirp` argument does not refer to an open directory stream. 145 * @retval EIO An I/O error occurred. 146 */ 147 void rewinddir(DIR* dirp); 148 /** 149 * @brief Gets the current position of a directory stream. 150 * 151 * @param dirp A pointer to a `DIR` structure returned by `opendir()`. 152 * 153 * @return The current position of the directory stream. This is the index of the last 154 * entry read by `readdir()`. Returns -1 if at the end of the directory stream. 155 * If -1 is returned and `errno` is not 0, an error occurred. 156 * 157 * @retval EBADF The `dirp` argument does not refer to an open directory stream. 158 * 159 * @note The position returned by `telldir()` is an opaque value that should only be 160 * used in conjunction with `seekdir()`. 161 */ 162 long telldir(DIR* dirp); 163 164 /** 165 * @brief Sets the position of a directory stream. 166 * 167 * @param dirp A pointer to a `DIR` structure returned by `opendir()`. 168 * @param loc The new position of the directory stream. The following values are supported: 169 * - **0:** Seek to the beginning of the stream (equivalent to `rewinddir()`). 170 * - **-1:** Seek to the end of the stream. 171 * - **\>0:** Seek to a specific entry by its index (the value returned by `telldir()`). 172 * 173 * @retval EBADF The `dirp` argument does not refer to an open directory stream. 174 * @retval EINVAL The `loc` argument is invalid (e.g., negative value other than -1, or a 175 * value that is greater than the number of entries in the directory). 176 * 177 * @note Seeking to arbitrary positions (other than the beginning or end) is implemented 178 * by rewinding the directory stream and then calling `readdir()` repeatedly until 179 * the desired position is reached. 180 * @note **Time Complexity:** 181 * - O(1) for `loc = 0` (rewind) and `loc = -1` (seek to end). 182 * - O(N) for `loc > 0`, where N is the position being sought to. In the worst case, 183 * seeking to the end of a large directory can be a slow operation. 184 */ 185 void seekdir(DIR* dirp, long loc); 186 187 #ifdef __cplusplus 188 } 189 #endif 190 191 #endif /* Not _AEMU_DIRENT_H_ */