diff options
Diffstat (limited to 'dir.c')
-rw-r--r-- | dir.c | 303 |
1 files changed, 0 insertions, 303 deletions
@@ -1,303 +0,0 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2021-2022 Tavian Barnes <tavianator@tavianator.com> * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ - -#include "dir.h" -#include "util.h" -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> - -#if __linux__ -# include <sys/syscall.h> -#endif // __linux__ - -enum bfs_type bfs_mode_to_type(mode_t mode) { - switch (mode & S_IFMT) { -#ifdef S_IFBLK - case S_IFBLK: - return BFS_BLK; -#endif -#ifdef S_IFCHR - case S_IFCHR: - return BFS_CHR; -#endif -#ifdef S_IFDIR - case S_IFDIR: - return BFS_DIR; -#endif -#ifdef S_IFDOOR - case S_IFDOOR: - return BFS_DOOR; -#endif -#ifdef S_IFIFO - case S_IFIFO: - return BFS_FIFO; -#endif -#ifdef S_IFLNK - case S_IFLNK: - return BFS_LNK; -#endif -#ifdef S_IFPORT - case S_IFPORT: - return BFS_PORT; -#endif -#ifdef S_IFREG - case S_IFREG: - return BFS_REG; -#endif -#ifdef S_IFSOCK - case S_IFSOCK: - return BFS_SOCK; -#endif -#ifdef S_IFWHT - case S_IFWHT: - return BFS_WHT; -#endif - - default: - return BFS_UNKNOWN; - } -} - -#if __linux__ -/** - * This is not defined in the kernel headers for some reason, callers have to - * define it themselves. - */ -struct linux_dirent64 { - ino64_t d_ino; - off64_t d_off; - unsigned short d_reclen; - unsigned char d_type; - char d_name[]; -}; - -// Make the whole allocation 64k -#define BUF_SIZE ((64 << 10) - 8) -#endif - -struct bfs_dir { -#if __linux__ - int fd; - unsigned short pos; - unsigned short size; -#else - DIR *dir; - struct dirent *de; -#endif -}; - -struct bfs_dir *bfs_opendir(int at_fd, const char *at_path) { -#if __linux__ - struct bfs_dir *dir = malloc(sizeof(*dir) + BUF_SIZE); -#else - struct bfs_dir *dir = malloc(sizeof(*dir)); -#endif - if (!dir) { - return NULL; - } - - int fd; - if (at_path) { - fd = openat(at_fd, at_path, O_RDONLY | O_CLOEXEC | O_DIRECTORY); - } else if (at_fd >= 0) { - fd = at_fd; - } else { - free(dir); - errno = EBADF; - return NULL; - } - - if (fd < 0) { - free(dir); - return NULL; - } - -#if __linux__ - dir->fd = fd; - dir->pos = 0; - dir->size = 0; -#else - dir->dir = fdopendir(fd); - if (!dir->dir) { - if (at_path) { - close_quietly(fd); - } - free(dir); - return NULL; - } - - dir->de = NULL; -#endif // __linux__ - - return dir; -} - -int bfs_dirfd(const struct bfs_dir *dir) { -#if __linux__ - return dir->fd; -#else - return dirfd(dir->dir); -#endif -} - -/** Convert a dirent type to a bfs_type. */ -static enum bfs_type translate_type(int d_type) { - switch (d_type) { -#ifdef DT_BLK - case DT_BLK: - return BFS_BLK; -#endif -#ifdef DT_CHR - case DT_CHR: - return BFS_CHR; -#endif -#ifdef DT_DIR - case DT_DIR: - return BFS_DIR; -#endif -#ifdef DT_DOOR - case DT_DOOR: - return BFS_DOOR; -#endif -#ifdef DT_FIFO - case DT_FIFO: - return BFS_FIFO; -#endif -#ifdef DT_LNK - case DT_LNK: - return BFS_LNK; -#endif -#ifdef DT_PORT - case DT_PORT: - return BFS_PORT; -#endif -#ifdef DT_REG - case DT_REG: - return BFS_REG; -#endif -#ifdef DT_SOCK - case DT_SOCK: - return BFS_SOCK; -#endif -#ifdef DT_WHT - case DT_WHT: - return BFS_WHT; -#endif - } - - return BFS_UNKNOWN; -} - -#if !__linux__ -/** Get the type from a struct dirent if it exists, and convert it. */ -static enum bfs_type dirent_type(const struct dirent *de) { -#if defined(_DIRENT_HAVE_D_TYPE) || defined(DT_UNKNOWN) - return translate_type(de->d_type); -#else - return BFS_UNKNOWN; -#endif -} -#endif - -/** Check if a name is . or .. */ -static bool is_dot(const char *name) { - return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); -} - -int bfs_readdir(struct bfs_dir *dir, struct bfs_dirent *de) { - while (true) { -#if __linux__ - char *buf = (char *)(dir + 1); - - if (dir->pos >= dir->size) { -#if BFS_HAS_FEATURE(memory_sanitizer, false) - // Make sure msan knows the buffer is initialized - memset(buf, 0, BUF_SIZE); -#endif - - ssize_t size = syscall(__NR_getdents64, dir->fd, buf, BUF_SIZE); - if (size <= 0) { - return size; - } - dir->pos = 0; - dir->size = size; - } - - const struct linux_dirent64 *lde = (void *)(buf + dir->pos); - dir->pos += lde->d_reclen; - - if (is_dot(lde->d_name)) { - continue; - } - - if (de) { - de->type = translate_type(lde->d_type); - de->name = lde->d_name; - } - - return 1; -#else // !__linux__ - errno = 0; - dir->de = readdir(dir->dir); - if (dir->de) { - if (is_dot(dir->de->d_name)) { - continue; - } - if (de) { - de->type = dirent_type(dir->de); - de->name = dir->de->d_name; - } - return 1; - } else if (errno != 0) { - return -1; - } else { - return 0; - } -#endif // !__linux__ - } -} - -int bfs_closedir(struct bfs_dir *dir) { -#if __linux__ - int ret = xclose(dir->fd); -#else - int ret = closedir(dir->dir); -#endif - free(dir); - return ret; -} - -int bfs_freedir(struct bfs_dir *dir) { -#if __linux__ - int ret = dir->fd; - free(dir); - return ret; -#elif __FreeBSD__ - int ret = fdclosedir(dir->dir); - free(dir); - return ret; -#else - int ret = dup_cloexec(dirfd(dir->dir)); - bfs_closedir(dir); - return ret; -#endif -} |