// Copyright 2007-2022 David Robillard // SPDX-License-Identifier: ISC #include "zix/filesystem.h" #include "path_iter.h" #include "system.h" #include "zix/allocator.h" #include "zix/status.h" #ifdef _WIN32 # include # include #else # include #endif #include #include #include #include #include #include #include ZixStatus zix_create_directories(ZixAllocator* const allocator, const char* const dir_path) { if (!dir_path[0]) { return ZIX_STATUS_BAD_ARG; } // Allocate a working copy of the path to chop along the way const size_t path_len = strlen(dir_path); char* const path = (char*)zix_malloc(allocator, path_len + 1U); ZixStatus st = path ? ZIX_STATUS_SUCCESS : ZIX_STATUS_NO_MEM; if (path) { // Copy directory path as prefix memcpy(path, dir_path, path_len + 1U); // Start at the root directory (past any name) ZixPathIter p = zix_path_begin(path); while (p.state < ZIX_PATH_FILE_NAME) { p = zix_path_next(path, p); } // Create each directory down the path while (p.state != ZIX_PATH_END) { char* const end = &path[p.range.end]; const char old_last = *end; *end = '\0'; if (zix_file_type(path) != ZIX_FILE_TYPE_DIRECTORY) { if ((st = zix_create_directory(path))) { break; } } *end = old_last; p = zix_path_next(path, p); } zix_free(allocator, path); } return st; } ZixFileOffset zix_file_size(const char* const path) { struct stat sb; return stat(path, &sb) ? (off_t)0 : sb.st_size; } bool zix_file_equals(ZixAllocator* const allocator, const char* const path_a, const char* const path_b) { if (!strcmp(path_a, path_b)) { return true; // Paths match } errno = 0; // Open files and get file information const int fd_a = zix_system_open_fd(path_a, O_RDONLY, 0); const int fd_b = zix_system_open_fd(path_b, O_RDONLY, 0); struct stat stat_a; struct stat stat_b; if (fd_a < 0 || fd_b < 0 || fstat(fd_a, &stat_a) || fstat(fd_b, &stat_b)) { zix_system_close_fds(fd_b, fd_a); return false; } bool match = false; if (stat_a.st_dev == stat_b.st_dev && stat_a.st_ino && stat_b.st_ino && stat_a.st_ino == stat_b.st_ino) { match = true; // Fast path: paths refer to the same file } else if (stat_a.st_size == stat_b.st_size) { // Slow path: files have equal size, compare contents const uint32_t size = zix_system_page_size(); void* const page_a = zix_aligned_alloc(allocator, size, size); void* const page_b = zix_aligned_alloc(allocator, size, size); if (page_a && page_b) { match = true; for (ZixSystemCountReturn n = 0; (n = read(fd_a, page_a, size)) > 0;) { if (read(fd_b, page_b, size) != n || !!memcmp(page_a, page_b, (size_t)n)) { match = false; break; } } } zix_aligned_free(allocator, page_b); zix_aligned_free(allocator, page_a); } return !zix_system_close_fds(fd_b, fd_a) && match; }