From 06950aab233de6a2f47293d59575bb42f6131660 Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Wed, 27 Jan 2021 22:06:49 +0100 Subject: Complete rewrite using C++ and with shared state support --- src/paths.c | 1029 ----------------------------------------------------------- 1 file changed, 1029 deletions(-) delete mode 100644 src/paths.c (limited to 'src/paths.c') diff --git a/src/paths.c b/src/paths.c deleted file mode 100644 index 1bdc4e3..0000000 --- a/src/paths.c +++ /dev/null @@ -1,1029 +0,0 @@ -#include "common.h" - -#include "paths.h" -#include "ref.h" -#include "thread.h" -#include "safe_fifo.h" -#include "strutil.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SEARCH_CACHE_SIZE (2) - -typedef struct search_t -{ - const char* dir; - const char** dirs; - char* match; - const char* match_name; - bool plain; - paths_find_callback_t find_callback; - void* userdata; -} search_t; - -typedef struct searchargs_t -{ - bool quit; - safe_fifo_t* queue; - thread_lock_t* lock; - search_t* cache[SEARCH_CACHE_SIZE]; -} searchargs_t; - -struct paths_t -{ - REF_DECLARE(); - char* user_data; - char* user_config; - char* user_cache; - char* user_runtime; - char** search_data; - char** search_config; - - searchargs_t* search; -#ifdef DEBUG - thread_t* searcher; -#endif -}; - -static void free_list(char** list); -static void free_search_data(search_t* search); -static bool paths_init(paths_t* paths); -static char* build_path(paths_t* paths, paths_source_t source, ...); -static bool create_dir(paths_t* paths, paths_source_t source, - const char* addon, size_t addon_len); -static void* searcher(void* _args); -static search_t* get_search(searchargs_t* args); -static void put_search(searchargs_t* args, search_t* search); - -paths_t* paths_new(void) -{ - paths_t* paths = calloc(1, sizeof(paths_t)); - if (!paths) - { - return NULL; - } - if (!REF_INIT(paths)) - { - free(paths); - return NULL; - } - for (;;) - { - paths->search = calloc(1, sizeof(searchargs_t)); - if (!paths->search) - { - break; - } - paths->search->lock = thread_lock_new(); - if (!paths->search->lock) - { - break; - } - paths->search->queue = safe_fifo_new(); - if (!paths->search->queue) - { - break; - } - safe_fifo_ref(paths->search->queue); -#ifdef DEBUG - paths->searcher = thread_joinable_new(searcher, paths->search); - if (!paths->searcher) - { - safe_fifo_unref(paths->search->queue); - break; - } -#else - if (!thread_new(searcher, paths->search)) - { - safe_fifo_unref(paths->search->queue); - break; - } -#endif - if (!paths_init(paths)) - { - break; - } - return paths; - } - paths_unref(paths); - return NULL; -} - -void paths_ref(paths_t* paths) -{ - assert(paths); - REF_INC(paths); -} - -void paths_unref(paths_t* paths) -{ - if (paths && REF_DEC(paths)) - { - if (paths->search) - { - if (paths->search->queue) - { - search_t* search = get_search(paths->search); - paths->search->quit = true; - if (search) - { - memset(search, 0, sizeof(search_t)); - safe_fifo_push(paths->search->queue, search); - } - safe_fifo_unref(paths->search->queue); - } -#ifdef DEBUG - thread_join(paths->searcher); -#endif - } - free(paths->user_data); - free(paths->user_config); - free(paths->user_cache); - free(paths->user_runtime); - free_list(paths->search_data); - free_list(paths->search_config); - REF_FREE(paths); - free(paths); - } -} - -const char* paths_user_dir(paths_t* paths, paths_source_t source) -{ - assert(paths); - switch (source) - { - case PATHS_DATA: - return paths->user_data; - case PATHS_CONFIG: - return paths->user_config; - case PATHS_CACHE: - return paths->user_cache; - case PATHS_RUNTIME: - return paths->user_runtime; - } - assert(false); - return NULL; -} - -const char** paths_search_dirs(paths_t* paths, paths_source_t source) -{ - assert(paths); - switch (source) - { - case PATHS_DATA: - return (const char**)paths->search_data; - case PATHS_CONFIG: - return (const char**)paths->search_config; - case PATHS_CACHE: - case PATHS_RUNTIME: - return NULL; - } - assert(false); - return NULL; -} - -struct paths_file_t -{ - paths_source_t source; - char* path, * target; - int fd; -}; - -paths_file_t* paths_write(paths_t* paths, paths_source_t source, - const char* filename, unsigned int flags, ...) -{ - paths_file_t* file; - int f; - const char* subpath; - assert(filename); - if (source == PATHS_RUNTIME && !paths->user_runtime) - { - errno = ENOENT; - return NULL; - } - if (filename[0] == '/') - { - filename++; - } - subpath = strrchr(filename, '/'); - if (subpath) - { - if (!create_dir(paths, source, filename, subpath - filename)) - { - return NULL; - } - } - else - { - if (!create_dir(paths, source, NULL, 0)) - { - return NULL; - } - } - file = calloc(1, sizeof(paths_file_t)); - if (!file) - { - return NULL; - } - file->source = source; - if (source == PATHS_CONFIG && - (((flags & PATHS_CREATE) && (flags & PATHS_TRUNCATE)) || - ((flags & PATHS_CREATE) && (flags & PATHS_EXCLUSIVE)))) - { - file->target = build_path(paths, source, filename, NULL); - if (!file->target) - { - free(file); - return NULL; - } - file->path = build_path(paths, source, ".#", filename, NULL); - } - else - { - file->path = build_path(paths, source, filename, NULL); - } - if (!file->path) - { - free(file->target); - free(file); - return NULL; - } - f = O_WRONLY; - if ((flags & PATHS_APPEND)) - { - f |= O_APPEND; - } - if ((flags & PATHS_CREATE)) - { - f |= O_CREAT; - if ((flags & PATHS_EXCLUSIVE)) - { - f |= O_EXCL; - } - } - if ((flags & PATHS_TRUNCATE)) - { - f |= O_TRUNC; - } -#ifdef O_EXLOCK - f |= O_EXLOCK; -#endif - if ((f & O_CREAT)) - { - va_list args; - va_start(args, flags); - file->fd = open(file->path, f, va_arg(args, int)); - va_end(args); - } - else - { - file->fd = open(file->path, f); - } - if (file->fd < 0) - { - free(file->path); - free(file->target); - free(file); - return NULL; - } -#ifndef O_EXLOCK - for (;;) - { - if (flock(file->fd, LOCK_EX) == 0) - { - break; - } - if (errno != EINTR) - { - if ((flags & O_CREAT)) - { - unlink(file->path); - } - close(file->fd); - free(file->path); - free(file->target); - free(file); - return NULL; - } - } -#endif - return file; -} - -ssize_t paths_file_write(paths_file_t* file, - const void* data, size_t size) -{ - size_t pos = 0; - assert(file); - assert(size == 0 || data); - while (pos < size) - { - ssize_t ret = write(file->fd, data + pos, size - pos); - if (ret < 0) - { - if (errno == EAGAIN || errno == EWOULDBLOCK || - errno == EINTR) - { - /* Non fatal errors */ - if (pos > 0) - { - return pos; - } - } - return -1; - } - if (ret == 0) - { - return pos; - } - pos += ret; - } - return size; -} - -int paths_file_close(paths_file_t* file) -{ - assert(file); - if (close(file->fd)) - { - return -1; - } - file->fd = -1; - if (file->target) - { - size_t len = strlen(file->target); - char* tmp = malloc(len + 2); - int ret; - if (!tmp) - { - return -1; - } - memcpy(tmp, file->target, len); - memcpy(tmp + len, "~", 2); - ret = rename(file->target, tmp); - free(tmp); - if (ret && errno == ENOENT) - { - int olderrno = errno; - /* Check if rename failed because target doesn't exist */ - if (access(file->target, F_OK) == 0 || - errno != ENOENT) - { - /* Unable to create backup */ - errno = olderrno; - return -1; - } - } - if (rename(file->path, file->target)) - { - return -1; - } - free(file->target); - } - free(file->path); - free(file); - return 0; -} - -void paths_file_abort(paths_file_t* file) -{ - if (file) - { - if (file->fd >= 0) - { - close(file->fd); - } - unlink(file->path); - free(file->path); - free(file->target); - free(file); - } -} - -bool create_dir(paths_t* paths, paths_source_t source, const char* addon, - size_t addon_len) -{ - const char* dir = paths_user_dir(paths, source); - char* tmp = NULL; - assert(dir); - assert(addon || !addon_len); - if (addon_len) - { - size_t dlen = strlen(dir); - tmp = malloc(dlen + 1 + addon_len + 1); - if (!tmp) - { - return false; - } - memcpy(tmp, dir, dlen); - tmp[dlen++] = '/'; - memcpy(tmp + dlen, addon, addon_len); - tmp[dlen + addon_len] = '\0'; - dir = tmp; - } - if (mkdir(dir, 0700)) - { - free(tmp); - return errno == EEXIST; - } - free(tmp); - return true; -} - -void free_list(char** list) -{ - if (list) - { - char** i; - for (i = list; *i; i++) - { - free(*i); - } - free(list); - } -} - -typedef struct homedir_t -{ - char* path; - size_t len; -} homedir_t; - -static char* get_userdir(const char* envvar, homedir_t* homedir, - const char* addon); -static char** get_searchdirs(const char* envvar, const char* default_list); - -static bool valid_runtime(const char* path); - -bool paths_init(paths_t* paths) -{ - homedir_t homedir = { NULL, 0 }; - paths->user_data = get_userdir("XDG_DATA_HOME", &homedir, "/.local/share"); - paths->user_config = get_userdir("XDG_CONFIG_HOME", &homedir, "/.config"); - paths->user_cache = get_userdir("XDG_CACHE_HOME", &homedir, "/.cache"); - paths->user_runtime = get_userdir("XDG_RUNTIME_HOME", NULL, NULL); - free(homedir.path); - if (!paths->user_data || !paths->user_config || !paths->user_cache) - { - return false; - } - if (paths->user_runtime) - { - if (!valid_runtime(paths->user_runtime)) - { - free(paths->user_runtime); - paths->user_runtime = NULL; - } - } - paths->search_data = get_searchdirs("XDG_DATA_DIRS", - "/usr/local/share:/usr/share"); - if (!paths->search_data) - { - return false; - } - paths->search_config = get_searchdirs("XDG_CONFIG_DIRS", "/etc/xdg"); - if (!paths->search_config) - { - return false; - } - return true; -} - -static void cleanup_dir(char* path, size_t len) -{ - while (len > 1 && path[len - 1] == '/') - { - path[--len] = '\0'; - } -} - -static char* get_homedir(void) -{ - const char* data = getenv("HOME"); - size_t len; - char* ret; - if (data && *data) - { - len = strlen(data); - ret = malloc(len + 1); - if (ret) - { - memcpy(ret, data, len + 1); - cleanup_dir(ret, len); - } - return ret; - } - for (;;) - { - long size = sysconf(_SC_GETPW_R_SIZE_MAX); - char* buf; - struct passwd entry, *result = NULL; - assert(size != -1); - if (size == -1) - { - break; - } - buf = malloc(size); - if (!buf) - { - break; - } - if (getpwuid_r(getuid(), &entry, buf, size, &result) != 0) - { - /* Error getting entry */ - free(buf); - break; - } - if (!result) - { - /* No entry found */ - free(buf); - break; - } - len = strlen(entry.pw_dir); - if (len == 0) - { - free(buf); - break; - } - ret = malloc(len + 1); - if (!ret) - { - free(buf); - return NULL; - } - memcpy(ret, entry.pw_dir, len + 1); - free(buf); - cleanup_dir(ret, len); - return ret; - } - /* When all else fails */ - return strdup("/"); -} - -char* get_userdir(const char* envvar, homedir_t* homedir, const char* addon) -{ - const char* data = getenv(envvar); - size_t len; - char* ret; - assert(!addon || addon[0] == '/'); - if (data && *data) - { - len = strlen(data); - ret = malloc(len + 1); - if (ret) - { - memcpy(ret, data, len + 1); - cleanup_dir(ret, len); - } - return ret; - } - if (!homedir && !addon) - { - return NULL; - } - assert(homedir && addon); - if (!homedir->path) - { - homedir->path = get_homedir(); - if (!homedir->path) - { - return NULL; - } - homedir->len = strlen(homedir->path); - } - len = homedir->len + strlen(addon); - ret = malloc(len + 1); - if (ret) - { - memcpy(ret, homedir->path, homedir->len); - if (homedir->len > 1) - { - assert(homedir->path[homedir->len - 1] != '/'); - memcpy(ret + homedir->len, addon, (len - homedir->len) + 1); - } - else - { - assert(homedir->path[0] == '/'); - len--; - memcpy(ret + homedir->len, addon + 1, len - homedir->len); - ret[len] = '\0'; - } - } - return ret; -} - -static void cleanup_dirs(char** dirs) -{ - char** d; - for (d = dirs; *d; d++) - { - cleanup_dir(*d, strlen(*d)); - } -} - -static char** splitlist(const char* str) -{ - size_t count = 0, size = 2; - char** ret = calloc(size + 1, sizeof(char*)); - const char* last = str, * pos; - if (!ret) - { - return NULL; - } - for (;;) - { - size_t len; - pos = strchr(last, ':'); - if (!pos) - { - pos = last + strlen(last); - } - - if (count == size) - { - size_t ns = size * 2; - char** tmp = realloc(ret, (ns + 1) * sizeof(char*)); - if (!tmp) - { - free_list(ret); - return NULL; - } - memset(tmp + size, 0, (ns + 1 - size) * sizeof(char*)); - ret = tmp; - size = ns; - } - len = pos - last; - ret[count] = strdup_len(last, len); - if (!ret[count]) - { - free_list(ret); - return NULL; - } - count++; - - if (*pos) - { - last = pos + 1; - } - else - { - break; - } - } - assert(!ret[count]); - return ret; -} - -char** get_searchdirs(const char* envvar, const char* default_list) -{ - const char* data = getenv(envvar); - char** ret; - bool cleanup; - if (data && *data) - { - ret = splitlist(data); - cleanup = true; - } - else - { - ret = splitlist(default_list); - cleanup = false; - } - if (!ret) - { - return NULL; - } - if (cleanup) - { - cleanup_dirs(ret); - } - return ret; -} - -bool valid_runtime(const char* path) -{ - struct stat buf; - if (stat(path, &buf) != 0) - { - return false; - } - if (buf.st_uid != getuid()) - { - return false; - } - if ((buf.st_mode & 0777) != 0700) - { - return false; - } - return true; -} - -char* build_path(paths_t* paths, paths_source_t source, ...) -{ - va_list args; - bool first = true; - dynstr_t* ret; - const char* base = paths_user_dir(paths, source), * part; - assert(base); - ret = dynstr_new_str(base); - if (!ret) - { - return NULL; - } - va_start(args, source); - while ((part = va_arg(args, const char*))) - { - if (first) - { - dynstr_appendc(ret, '/'); - first = false; - } - dynstr_append(ret, part); - } - return dynstr_done(ret); -} - -static bool need_glob(char* match) -{ - const char* magic_chars = "*?[]{}"; - char* s; - bool need_unescape = false; - for (s = match; *s; s++) - { - if (*s == '\\') - { - s++; - need_unescape = true; - } - else if (strchr(magic_chars, *s)) - { - return true; - } - } - if (need_unescape) - { - unescape(match); - } - return false; -} - -void paths_find(paths_t* paths, paths_source_t source, - const char* match, paths_find_callback_t find_callback, - void* userdata) -{ - search_t* search; - search = get_search(paths->search); - search->dir = paths_user_dir(paths, source); - search->dirs = paths_search_dirs(paths, source); - search->match = strdup(match); - search->find_callback = find_callback; - search->userdata = userdata; - if (!search->match) - { - find_callback(userdata, NULL); - put_search(paths->search, search); - return; - } - if (*search->match == '/') - { - size_t len = strlen(search->match); - memmove(search->match, search->match + 1, len); - } - if (!*search->match) - { - find_callback(userdata, NULL); - put_search(paths->search, search); - return; - } - search->plain = !need_glob(search->match); - if (search->plain) - { - search->match_name = strrchr(search->match, '/'); - if (search->match_name) - { - search->match_name++; - } - else - { - search->match_name = search->match; - } - } - safe_fifo_push(paths->search->queue, search); -} - -search_t* get_search(searchargs_t* args) -{ - size_t i; - thread_lock_lock(args->lock); - for (i = 0; i < SEARCH_CACHE_SIZE; i++) - { - if (args->cache[i]) - { - search_t* ret = args->cache[i]; - args->cache[i] = NULL; - thread_lock_unlock(args->lock); - return ret; - } - } - thread_lock_unlock(args->lock); - return malloc(sizeof(search_t)); -} - -void put_search(searchargs_t* args, search_t* search) -{ - size_t i; - thread_lock_lock(args->lock); - for (i = 0; i < SEARCH_CACHE_SIZE; i++) - { - if (!args->cache[i]) - { - args->cache[i] = search; - free_search_data(search); - thread_lock_unlock(args->lock); - return; - } - } - thread_lock_unlock(args->lock); - free_search_data(search); - free(search); -} - -void free_search_data(search_t* search) -{ - free(search->match); -} - -static bool search_dir(search_t* search, const char* dir) -{ - bool canceled = false; - if (search->plain) - { - DIR* dh; - struct dirent* d; - char* path = NULL; - size_t dirlen = strlen(dir); - size_t matchlen = strlen(search->match); - if (search->match == search->match_name) - { - path = malloc(dirlen + 1 + matchlen + 1); - if (!path) - { - return false; - } - memcpy(path, dir, dirlen + 1); - } - else - { - size_t subdirlen = search->match_name - 1 - search->match; - matchlen = strlen(search->match_name); - path = malloc(dirlen + 1 + subdirlen + 1 + matchlen + 1); - assert(search->match_name > search->match); - assert(search->match_name[-1] == '/'); - if (!path) - { - return false; - } - memcpy(path, dir, dirlen); - path[dirlen++] = '/'; - memcpy(path + dirlen, search->match, subdirlen); - dirlen += subdirlen; - path[dirlen] = '\0'; - } - dh = opendir(path); - if (!dh) - { - free(path); - return false; - } - path[dirlen] = '/'; - while ((d = readdir(dh))) - { - if (strcmp(d->d_name, search->match_name) == 0) - { - memcpy(path + dirlen + 1, search->match_name, matchlen + 1); - if (!search->find_callback(search->userdata, path)) - { - canceled = true; - break; - } - } - } - closedir(dh); - free(path); - } - else - { - glob_t data; - dynstr_t* str; - char* pattern; - str = dynstr_new_str(dir); - if (!str) - { - return false; - } - if (!dynstr_escape(str, "*?[]{}") || !dynstr_appendc(str, '/') || - !dynstr_append(str, search->match)) - { - dynstr_free(str); - return false; - } - pattern = dynstr_done(str); - memset(&data, 0, sizeof(glob_t)); - if (glob(pattern, GLOB_NOSORT, NULL, &data) == 0) - { - size_t i; - for (i = 0; i < data.gl_pathc; i++) - { - if (!search->find_callback(search->userdata, data.gl_pathv[i])) - { - canceled = true; - break; - } - } - globfree(&data); - } - free(pattern); - } - return canceled; -} - -void* searcher(void* _args) -{ - searchargs_t* args = _args; - size_t i; - - while (!args->quit) - { - search_t* search = safe_fifo_pop(args->queue); - bool canceled = false; - if (!search->match) - { - free(search); - assert(args->quit); - break; - } - do - { - if (search->dir) - { - if (args->quit || - (canceled = search_dir(search, search->dir))) - { - break; - } - } - if (search->dirs) - { - const char** dir; - for (dir = search->dirs; *dir; dir++) - { - if (args->quit || - (canceled = search_dir(search, *dir))) - { - break; - } - } - } - } while (false); - if (!canceled) - { - search->find_callback(search->userdata, NULL); - } - put_search(args, search); - } - - /* Free all items in the queue */ - for (;;) - { - search_t* search = safe_fifo_trypop(args->queue); - if (!search) - { - break; - } - put_search(args, search); - } - - safe_fifo_unref(args->queue); - thread_lock_free(args->lock); - for (i = 0; i < SEARCH_CACHE_SIZE; i++) - { - if (args->cache[i]) - { - free_search_data(args->cache[i]); - free(args->cache[i]); - } - } - free(args); - return NULL; -} -- cgit v1.2.3-70-g09d2