ali@475: /* ali@475: * Copyright (C) 2008 Kristian Høgsberg ali@475: * Copyright (C) 2008 Red Hat, Inc ali@475: * Copyright (C) 2009, 2011, 2012, 2014, 2016 J. Ali Harlow ali@475: * ali@475: * This program is free software; you can redistribute it and/or modify ali@475: * it under the terms of the GNU General Public License as published by ali@475: * the Free Software Foundation; either version 2 of the License, or ali@475: * (at your option) any later version. ali@475: * ali@475: * This program is distributed in the hope that it will be useful, ali@475: * but WITHOUT ANY WARRANTY; without even the implied warranty of ali@475: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ali@475: * GNU General Public License for more details. ali@475: * ali@475: * You should have received a copy of the GNU General Public License along ali@475: * with this program; if not, write to the Free Software Foundation, Inc., ali@475: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ali@475: */ ali@475: ali@475: #include "config.h" ali@475: ali@475: #include ali@475: #include ali@475: #include ali@475: #include ali@475: #include ali@475: #include ali@475: #include ali@475: #include ali@475: #include ali@475: #include ali@475: #include ali@475: #ifdef MSWIN_API ali@475: #include ali@475: #include ali@475: #endif ali@475: #if HAVE_SYS_MMAN_H ali@475: #include ali@475: #endif ali@476: #if HAVE_LIBARCHIVE ali@476: #include ali@476: #include ali@476: #endif ali@475: #include ali@475: ali@475: #include "razor.h" ali@475: #include "types/types.h" ali@475: #include "razor-internal.h" ali@475: ali@475: #ifndef O_BINARY ali@475: #define O_BINARY 0 ali@475: #endif ali@475: ali@475: #define strcmp0(s1, s2) ((s1) && (s2) ? strcmp(s1, s2) : \ ali@475: (s1) ? 1 : (s2) ? -1 : 0) ali@475: ali@476: static void * ali@476: razor_archive_get_file_contents(const char *filename, int fd, const char *path, ali@476: size_t *length, struct razor_error **error) ali@476: { ali@483: #if HAVE_LIBARCHIVE ali@476: int r; ali@483: const char *errmsg; ali@476: void *addr; ali@476: const void *buf; ali@476: size_t size; ali@476: off_t offset; ali@476: struct archive *a; ali@476: struct archive_entry *entry; ali@476: ali@476: a = archive_read_new(); ali@476: archive_read_support_compression_all(a); ali@476: archive_read_support_format_all(a); ali@476: ali@476: r = archive_read_open_fd(a, fd, 10240); ali@476: ali@476: if (r) { ali@483: errmsg = archive_error_string(a); ali@483: if (!strcmp(errmsg, "Unrecognized archive format")) ali@483: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@483: RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE, ali@483: filename, errmsg); ali@483: else ali@483: razor_set_error(error, RAZOR_POSIX_ERROR, ali@483: archive_errno(a), filename, errmsg); ali@476: archive_read_finish(a); ali@476: return NULL; ali@476: } ali@476: ali@476: for (;;) { ali@476: r = archive_read_next_header(a, &entry); ali@476: if (r == ARCHIVE_EOF) ali@476: break; ali@476: else if (r < ARCHIVE_WARN) { ali@476: razor_set_error(error, RAZOR_POSIX_ERROR, ali@476: archive_errno(a), filename, ali@476: archive_error_string(a)); ali@476: archive_read_close(a); ali@476: archive_read_finish(a); ali@476: return NULL; ali@476: } ali@476: ali@476: /* ali@476: * TODO: Unicode support. Might need to wait for libarchive v4. ali@476: */ ali@476: if (!strcmp(archive_entry_pathname(entry), path)) { ali@476: addr = malloc(archive_entry_size(entry)); ali@476: if (!addr) { ali@476: archive_read_close(a); ali@476: archive_read_finish(a); ali@476: razor_set_error(error, RAZOR_POSIX_ERROR, ali@476: ENOMEM, NULL, ali@476: "Not enough memory"); ali@476: return NULL; ali@476: } ali@476: ali@476: for(;;) { ali@476: r = archive_read_data_block(a, &buf, &size, ali@476: &offset); ali@476: if (r == ARCHIVE_EOF) ali@476: break; ali@476: if (r < ARCHIVE_OK) { ali@476: razor_set_error(error, RAZOR_POSIX_ERROR, ali@476: archive_errno(a), path, ali@476: archive_error_string(a)); ali@476: free(addr); ali@476: addr = NULL; ali@476: break; ali@476: } ali@476: memcpy((char *)addr + offset, buf, size); ali@476: } ali@476: ali@476: archive_read_close(a); ali@476: archive_read_finish(a); ali@476: ali@476: return addr; ali@476: } ali@476: } ali@476: ali@476: archive_read_close(a); ali@476: archive_read_finish(a); ali@476: ali@476: razor_set_error(error, RAZOR_POSIX_ERROR, ENOENT, path, ali@476: "No such file or directory in archive"); ali@476: ali@476: return NULL; ali@483: #else ali@483: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@483: RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE, ali@483: filename, "Archives are not supported in this build"); ali@483: ali@483: return NULL; ali@483: #endif /* HAVE_LIBARCHIVE */ ali@476: } ali@476: ali@476: static void * ali@476: razor_file_get_contents_archive(const char *filename, size_t *length, ali@476: struct razor_error **error) ali@476: { ali@476: int fd; ali@476: char *path, *slash, *s; ali@476: void *addr; ali@483: struct razor_error *tmp_error = NULL; ali@476: ali@476: path = strdup(filename); ali@476: slash = strrchr(path, '/'); ali@476: ali@476: while (slash) { ali@476: *slash = '\0'; ali@476: fd = open(path, O_RDONLY | O_BINARY); ali@476: if (fd >= 0) { ali@476: addr = razor_archive_get_file_contents(path, fd, ali@476: slash + 1, ali@483: length, ali@483: error); ali@476: free(path); ali@476: close(fd); ali@476: return addr; ali@481: #ifdef MSWIN_API ali@481: } else if (errno != ENOTDIR && errno != ENOENT) { ali@481: #else ali@476: } else if (errno != ENOTDIR) { ali@481: #endif ali@476: free(path); ali@476: razor_set_error_posix(error, filename); ali@476: return NULL; ali@476: } ali@476: s = strrchr(path, '/'); ali@476: *slash = '/'; ali@476: slash = s; ali@476: } ali@476: ali@476: free(path); ali@481: #ifdef MSWIN_API ali@481: razor_set_error(error, RAZOR_POSIX_ERROR, ENOENT, filename, ali@481: strerror(ENOENT)); ali@481: #else ali@476: razor_set_error(error, RAZOR_POSIX_ERROR, ENOTDIR, filename, ali@476: strerror(ENOTDIR)); ali@481: #endif ali@476: return NULL; ali@476: } ali@476: ali@475: #define OPEN_FILE_USED (1U<<0) ali@475: #define OPEN_FILE_MMAPPED (1U<<1) ali@475: ali@475: struct open_file { ali@475: void *addr; ali@475: size_t length; ali@475: uint32_t flags; ali@475: }; ali@475: ali@475: struct array open_files = { 0, }; ali@475: ali@475: void *razor_file_get_contents(const char *filename, size_t *length, int private, ali@475: struct razor_error **error) ali@475: { ali@475: int fd; ali@475: struct stat st; ali@475: void *addr = NULL; ali@476: size_t nb, size; ali@475: ssize_t res; ali@475: struct open_file *of, *ofend; ali@475: ali@475: fd = open(filename, O_RDONLY | O_BINARY); ali@475: if (fd < 0) { ali@481: #ifdef MSWIN_API ali@481: if (errno != ENOTDIR && errno != ENOENT) { ali@481: #else ali@476: if (errno != ENOTDIR) { ali@481: #endif ali@476: razor_set_error_posix(error, filename); ali@476: return NULL; ali@476: } ali@476: addr = razor_file_get_contents_archive(filename, &size, error); ali@476: if (!addr) ali@476: return NULL; ali@476: } else { ali@476: if (fstat(fd, &st) < 0) { ali@476: razor_set_error_posix(error, filename); ali@476: close(fd); ali@476: return NULL; ali@476: } ali@476: size = st.st_size; ali@475: } ali@475: ali@475: ofend = open_files.data + open_files.size; ali@475: for (of = open_files.data; of < ofend; of++) ali@475: if (!(of->flags & OPEN_FILE_USED)) ali@475: break; ali@475: if (of == ofend) { ali@475: of = array_add(&open_files, sizeof *of); ali@475: of->flags = 0; ali@475: } ali@475: ali@475: #if HAVE_SYS_MMAN_H ali@476: if (!addr && !private) { ali@476: addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); ali@475: if (addr == MAP_FAILED) ali@475: addr = NULL; ali@475: else ali@475: of->flags = OPEN_FILE_USED | OPEN_FILE_MMAPPED; ali@475: } ali@475: #endif /* HAVE_SYS_MMAN_H */ ali@476: ali@475: if (!addr) { ali@476: addr = malloc(size); ali@475: if (addr) { ali@475: nb = 0; ali@476: while(nb < size) { ali@476: res = read(fd, addr + nb, size - nb); ali@475: if (res <= 0) { ali@475: razor_set_error_posix(error, filename); ali@475: free(addr); ali@475: addr = NULL; ali@475: break; ali@475: } ali@475: nb += res; ali@475: } ali@476: if (addr) ali@476: of->flags = OPEN_FILE_USED; ali@475: } else ali@475: razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL, ali@475: "Not enough memory"); ali@475: } ali@476: ali@476: if (fd >= 0) ali@476: close(fd); ali@475: ali@475: of->addr = addr; ali@476: of->length = size; ali@476: ali@476: if (addr) ali@476: *length = size; ali@475: ali@475: return addr; ali@475: } ali@475: ali@475: int razor_file_free_contents(void *addr, size_t length) ali@475: { ali@475: #if HAVE_SYS_MMAN_H ali@475: int mmapped; ali@475: #endif ali@475: struct open_file *of, *ofend; ali@475: ali@475: ofend = open_files.data + open_files.size; ali@475: for (of = open_files.data; of < ofend; of++) ali@475: if ((of->flags & OPEN_FILE_USED) && of->addr == addr) ali@475: break; ali@475: ali@475: if (of == ofend) ali@475: return -2; ali@475: ali@475: length = of->length; ali@475: #if HAVE_SYS_MMAN_H ali@475: mmapped = of->flags & OPEN_FILE_MMAPPED; ali@475: #endif ali@475: of->flags &= ~OPEN_FILE_USED; ali@475: ali@475: for (of = open_files.data; of < ofend; of++) ali@475: if (of->flags & OPEN_FILE_USED) ali@475: break; ali@475: ali@475: if (of == ofend) { ali@475: array_release(&open_files); ali@475: array_init(&open_files); ali@475: } ali@475: ali@475: #if HAVE_SYS_MMAN_H ali@475: if (mmapped) ali@475: return munmap(addr, length); ali@475: #endif ali@475: ali@475: free(addr); ali@475: return 0; ali@475: } ali@475: ali@475: int razor_file_mkdir(const char *path, mode_t mode, struct razor_error **error) ali@475: { ali@475: int retval, code; ali@475: struct stat buf; ali@475: ali@475: retval = mkdir(path, mode); ali@475: ali@475: if (retval) { ali@475: /* ali@475: * Ignore the case of a pre-existing directory and give ali@475: * an explicit error message if there is something other ali@475: * than a directory already at path. ali@475: */ ali@475: code = errno; ali@475: if (code != EEXIST || stat(path, &buf)) ali@475: razor_set_error(error, RAZOR_POSIX_ERROR, code, path, ali@475: strerror(code)); ali@475: else if (!S_ISDIR(buf.st_mode)) ali@475: razor_set_error(error, RAZOR_POSIX_ERROR, code, path, ali@475: "Not a directory"); ali@475: } ali@475: ali@475: return retval; ali@475: } ali@475: ali@475: int razor_file_unlink(const char *path, struct razor_error **error) ali@475: { ali@475: int retval; ali@475: ali@475: retval = unlink(path); ali@475: ali@475: if (retval) ali@475: razor_set_error_posix(error, path); ali@475: ali@475: return retval; ali@475: } ali@475: ali@475: int razor_file_open(const char *path, int flags, mode_t mode, ali@475: struct razor_error **error) ali@475: { ali@475: int retval; ali@475: ali@475: retval = open(path, flags, mode); ali@475: ali@475: if (retval < 0) ali@475: razor_set_error_posix(error, path); ali@475: ali@475: return retval; ali@475: } ali@475: ali@475: int razor_file_move(const char *path, const char *dest, ali@475: struct razor_error **error) ali@475: { ali@475: int retval = 0; ali@475: ali@475: #ifdef MSWIN_API ali@475: wchar_t *oldbuf, *newbuf; ali@475: const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING; ali@475: ali@475: newbuf = razor_utf8_to_utf16(dest, -1); ali@475: oldbuf = razor_utf8_to_utf16(path, -1); ali@475: ali@475: /* ali@475: * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will ali@475: * cover every case we care about _except_ replacing an empty ali@475: * directory with a file. Calling RemoveDirectory() will deal ali@475: * with this case while having no effect in all other cases. ali@475: */ ali@475: (void)RemoveDirectoryW(newbuf); ali@475: ali@475: if (!MoveFileExW(oldbuf, newbuf, flags)) { ali@475: razor_set_error_mswin(error, newbuf, GetLastError()); ali@475: retval = -1; ali@475: } ali@475: ali@475: free(newbuf); ali@475: free(oldbuf); ali@475: #else ali@475: int code; ali@475: const char *object; ali@475: ali@475: if (rename(path, dest)) { ali@475: if (error) { ali@475: code = errno; ali@475: if (access(path, F_OK) < 0) ali@475: object = path; ali@475: else ali@475: object = dest; ali@475: razor_set_error(error, RAZOR_POSIX_ERROR, code, object, ali@475: strerror(code)); ali@475: } ali@475: retval = -1; ali@475: } ali@475: #endif ali@475: ali@475: return retval; ali@475: } ali@475: ali@475: #ifndef MSWIN_API ali@475: static char *absolute_path(const char *path) ali@475: { ali@475: int len; ali@475: char *result, *subpath, *p, *s, *t; ali@475: ali@475: result = realpath(path, NULL); ali@475: ali@475: if (!result && errno == ENOENT) { ali@475: p = strdup(path); ali@475: s = strrchr(p, '/'); ali@475: ali@475: while (s) { ali@475: if (s == p) { ali@475: result = strdup("/"); ali@475: break; ali@475: } ali@475: ali@475: *s = '\0'; ali@475: subpath = realpath(p, NULL); ali@475: ali@475: if (subpath) { ali@475: *s = '/'; ali@475: len = strlen(subpath); ali@475: result = malloc(len + strlen(s) + 1); ali@475: memcpy(result, subpath, len); ali@475: strcpy(result + len, s); ali@475: free(subpath); ali@475: break; ali@475: } else if (errno != ENOENT) ali@475: break; ali@475: ali@475: t = strrchr(p, '/'); ali@475: *s = '/'; ali@475: s = t; ali@475: } ali@475: ali@475: if (!s) ali@475: result = realpath(".", NULL); ali@475: ali@475: free(p); ali@475: } ali@475: ali@475: return result; ali@475: } ali@475: #endif ali@475: ali@475: int razor_file_is_directory(const char *path, struct razor_error **error) ali@475: { ali@475: struct stat st; ali@475: ali@475: if (stat(path, &st) < 0) { ali@475: razor_set_error_posix(error, path); ali@475: return -1; ali@475: } ali@475: ali@475: return !!S_ISDIR(st.st_mode); ali@475: } ali@475: ali@475: char *razor_file_mkdtemp_near(const char *path, const char *template, ali@475: struct razor_error **error) ali@475: { ali@475: char *retval; ali@475: ali@475: #ifdef MSWIN_API ali@475: if (path[0]=='\\' && path[1]=='\\' && path[2] && path[2]!='\\' ali@475: && strchr(path+3,'\\')) { ali@475: /* We have a UNC path: \\servername\sharename... */ ali@475: const char *sharename, *root; ali@475: int disklen; ali@475: ali@475: sharename = strchr(path+3,'\\')+1; ali@475: root = strchr(sharename,'\\'); ali@475: if (root) ali@475: disklen = root - path; ali@475: else ali@475: disklen = strlen(path); ali@475: ali@475: retval = malloc(disklen + 1 + strlen(template) + 1); ali@475: memcpy(retval, path, disklen); ali@475: retval[disklen] = '\\'; ali@475: strcpy(retval + disklen + 1, template); ali@475: } else if ((*path>='A' && *path<='Z' || *path>='a' && *path<='z') && ali@475: path[1]==':') { ali@475: retval = malloc(3 + strlen(template) + 1); ali@475: retval[0] = path[0]; ali@475: retval[1] = ':'; ali@475: retval[2] = '\\'; ali@475: strcpy(retval + 3, template); ali@475: } else { ali@475: DWORD n; ali@475: wchar_t *buf; ali@475: char *dir; ali@475: ali@475: n = GetCurrentDirectoryW(0, NULL); ali@475: buf = malloc(n * sizeof(wchar_t)); ali@475: ali@475: if (GetCurrentDirectoryW(n, buf)) { ali@475: dir = razor_utf16_to_utf8(buf, n - 1); ali@475: free(buf); ali@475: retval = razor_file_mkdtemp_near(dir, template, error); ali@475: free(dir); ali@475: return retval; ali@475: } else { ali@475: retval = malloc(3 + strlen(template) + 1); ali@475: retval[0] = 'C'; ali@475: retval[1] = ':'; ali@475: retval[2] = '\\'; ali@475: strcpy(retval + 3, template); ali@475: } ali@475: ali@475: free(buf); ali@475: } ali@475: #else ali@475: /* ali@475: * Find the mount point (assuming we can write to the ali@475: * whole filesystem). Otherwise stop at the first ali@475: * unwritable directory and take one step back. ali@475: */ ali@475: char *s, *abspath, saved; ali@475: int len, can_step_back = 0; ali@475: dev_t filesystem; ali@475: struct stat buf; ali@475: ali@475: abspath = absolute_path(path); ali@475: if (!abspath) { ali@475: razor_set_error_posix(error, path); ali@475: return NULL; ali@475: } ali@475: ali@475: if (stat(abspath, &buf) < 0) { ali@475: if (errno == ENOENT) ali@475: filesystem = 0; ali@475: else { ali@475: razor_set_error_posix(error, abspath); ali@475: free(abspath); ali@475: return NULL; ali@475: } ali@475: } else ali@475: filesystem = buf.st_dev; ali@475: ali@475: len = strlen(abspath); ali@475: while(len > 1 && (s = strrchr(abspath, '/'))) { ali@475: if (s == abspath) { ali@475: saved = s[1]; ali@475: s[1] = '\0'; ali@475: len = s + 1 - abspath; ali@475: } else { ali@475: s[0] = '\0'; ali@475: len = s - abspath; ali@475: } ali@475: ali@475: if (stat(abspath, &buf) < 0) { ali@475: if (errno == ENOENT) ali@475: continue; ali@475: else { ali@475: razor_set_error_posix(error, abspath); ali@475: free(abspath); ali@475: return NULL; ali@475: } ali@475: } else if (!filesystem) ali@475: filesystem = buf.st_dev; ali@475: ali@475: if (buf.st_dev != filesystem || access(abspath, W_OK)) { ali@475: if (can_step_back) { ali@475: if (s == abspath) ali@475: s[1] = saved; ali@475: else ali@475: s[0] = '/'; ali@475: } ali@475: len = strlen(abspath); ali@475: break; ali@475: } else ali@475: can_step_back = 1; ali@475: } ali@475: ali@475: if (len == 1) ali@475: len = 0; /* Avoid an unslightly double slash. */ ali@475: retval = malloc(len + 1 + strlen(template) + 1); ali@475: memcpy(retval, abspath, len); ali@475: retval[len] = '/'; ali@475: strcpy(retval + len + 1, template); ali@475: ali@475: free(abspath); ali@475: #endif ali@475: ali@475: if (!mkdtemp(retval)) { ali@475: int err = errno; ali@475: ali@475: #ifdef EACCES ali@475: if (err == EACCES) { ali@475: char *s = strdup(template); ali@475: ali@475: #ifndef MSWIN_API ali@475: if (stat(".", &buf) < 0) { ali@475: razor_set_error_posix(error, "."); ali@475: free(s); ali@475: free(retval); ali@475: return NULL; ali@475: } ali@475: if (buf.st_dev != filesystem) ali@475: /* ali@475: * Don't use a different filesystem. It will ali@475: * only fail later on (in rename) and cause ali@475: * an unhelpful error message (EXDEV). ali@475: */ ali@475: free(s); ali@475: else ali@475: #endif ali@475: if (mkdtemp(s)) { ali@475: free(retval); ali@475: retval = s; ali@475: return retval; ali@475: } else ali@475: free(s); ali@475: } ali@475: #endif ali@475: ali@475: razor_set_error(error, RAZOR_POSIX_ERROR, err, retval, ali@475: strerror(err)); ali@475: ali@475: free(retval); ali@475: retval = NULL; ali@475: } ali@475: ali@475: return retval; ali@475: } ali@475: ali@475: struct open_dir { ali@475: uint32_t flags; ali@475: #ifdef MSWIN_API ali@475: _WDIR *dp; ali@475: wchar_t *path2; ali@475: #else ali@475: DIR *dp; ali@475: char *path; ali@475: #endif ali@475: }; ali@475: ali@475: #define OPEN_DIR_USED (1U<<0) ali@475: ali@475: struct array open_dirs = { 0, }; ali@475: ali@475: void *razor_file_opendir(const char *path, struct razor_error **error) ali@475: { ali@475: struct open_dir *od, *odend; ali@475: ali@475: odend = open_dirs.data + open_dirs.size; ali@475: for (od = open_dirs.data; od < odend; od++) ali@475: if (!(od->flags & OPEN_DIR_USED)) ali@475: break; ali@475: if (od == odend) { ali@475: od = array_add(&open_dirs, sizeof *od); ali@475: od->flags = 0; ali@475: } ali@475: ali@475: #ifdef MSWIN_API ali@475: od->path2 = razor_utf8_to_utf16(path, -1); ali@475: od->dp = _wopendir(od->path2); ali@475: #else ali@475: od->path = strdup(path); ali@475: od->dp = opendir(od->path); ali@475: #endif ali@475: ali@475: if (!od->dp) { ali@475: #ifdef MSWIN_API ali@475: razor_set_error_mswin(error, od->path2, GetLastError()); ali@475: free(od->path2); ali@475: #else ali@475: razor_set_error_posix(error, od->path); ali@475: free(od->path); ali@475: #endif ali@475: return NULL; ali@475: } ali@475: ali@475: od->flags = OPEN_DIR_USED; ali@475: return od; ali@475: } ali@475: ali@475: char *razor_file_readdir(void *dp, struct razor_error **error) ali@475: { ali@475: struct open_dir *od = dp, *odend; ali@475: #ifdef MSWIN_API ali@475: struct _wdirent *dirp; ali@475: char *path; ali@475: #else ali@475: struct dirent *dirp; ali@475: #endif ali@475: ali@475: odend = open_dirs.data + open_dirs.size; ali@475: if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED)) ali@475: return (char *)-1; ali@475: ali@475: errno = 0; ali@475: ali@475: #ifdef MSWIN_API ali@475: while((dirp = _wreaddir(od->dp))) { ali@475: path = razor_utf16_to_utf8(dirp->d_name, -1); ali@475: if (strcmp(path, ".") && strcmp(path, "..")) ali@475: return path; ali@475: else ali@475: free(path); ali@475: } ali@475: #else ali@475: while((dirp = readdir(od->dp))) ali@475: if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) ali@475: return strdup(dirp->d_name); ali@475: #endif ali@475: ali@475: if (errno) { ali@475: #ifdef MSWIN_API ali@475: razor_set_error_mswin(error, od->path2, GetLastError()); ali@475: #else ali@475: razor_set_error_posix(error, od->path); ali@475: #endif ali@475: } ali@475: ali@475: return NULL; ali@475: } ali@475: ali@475: int razor_file_closedir(void *dp, struct razor_error **error) ali@475: { ali@475: struct open_dir *od = dp, *odend; ali@475: int retval; ali@475: ali@475: odend = open_dirs.data + open_dirs.size; ali@475: if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED)) ali@475: return -2; ali@475: ali@475: #ifdef MSWIN_API ali@475: /* ali@475: * I can't find documentation to state that _wclosedir() ali@475: * returns -1 on failure, so be paranoid. ali@475: */ ali@475: retval = _wclosedir(od->dp) ? -1 : 0; ali@475: #else ali@475: retval = closedir(od->dp); ali@475: #endif ali@475: ali@475: if (retval) { ali@475: #ifdef MSWIN_API ali@475: razor_set_error_mswin(error, od->path2, GetLastError()); ali@475: #else ali@475: razor_set_error_posix(error, od->path); ali@475: #endif ali@475: } ali@475: ali@475: #ifdef MSWIN_API ali@475: free(od->path2); ali@475: #else ali@475: free(od->path); ali@475: #endif ali@475: ali@475: od->flags &= ~OPEN_DIR_USED; ali@475: ali@475: for (od = open_dirs.data; od < odend; od++) ali@475: if (od->flags & OPEN_DIR_USED) ali@475: break; ali@475: ali@475: if (od == odend) { ali@475: array_release(&open_dirs); ali@475: array_init(&open_dirs); ali@475: } ali@475: ali@475: return retval; ali@475: } ali@475: ali@475: struct razor_uri_vtable_entry { ali@475: struct razor_uri_vtable vtable; ali@475: char *scheme; ali@475: struct array open_files, open_directories; ali@475: }; ali@475: ali@475: static struct array razor_uri_vtable_entries; ali@475: ali@475: static struct razor_uri_vtable_entry * ali@475: razor_uri_get_vtable_entry(const char *scheme) ali@475: { ali@475: struct razor_uri_vtable_entry *entry, *eend, *fallback = NULL; ali@475: ali@475: eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size; ali@475: for(entry = razor_uri_vtable_entries.data; entry < eend; entry++) { ali@475: if (!strcmp0(entry->scheme, scheme)) ali@475: return entry; ali@475: else if (!entry->scheme) ali@475: fallback = entry; ali@475: } ali@475: ali@475: return fallback; ali@475: } ali@475: ali@476: RAZOR_EXPORT int ali@476: razor_uri_mkdir(const char *uri, mode_t mode, struct razor_error **error) ali@475: { ali@475: int retval; ali@475: char *path; ali@475: struct razor_uri ru; ali@475: struct razor_uri_vtable_entry *entry; ali@475: struct razor_error *tmp_error = NULL; ali@475: ali@475: if (razor_uri_parse(&ru, uri, error)) ali@475: return -1; ali@475: ali@475: path = razor_path_from_parsed_uri(&ru, &tmp_error); ali@475: ali@475: if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI)) ali@475: razor_error_free(tmp_error); ali@475: else if (!path) { ali@475: razor_propagate_error(error, tmp_error, NULL); ali@475: razor_uri_destroy(&ru); ali@475: return -1; ali@475: } ali@475: ali@475: if (path) { ali@475: razor_uri_destroy(&ru); ali@475: retval = razor_file_mkdir(path, mode, error); ali@475: free(path); ali@475: } else { ali@475: entry = razor_uri_get_vtable_entry(ru.scheme); ali@475: razor_uri_destroy(&ru); ali@475: if (!entry || !entry->vtable.mkdir) { ali@475: retval = -1; ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI, ali@475: uri, "No URI handler installed"); ali@475: } else ali@475: retval = entry->vtable.mkdir(uri, mode, error); ali@475: } ali@475: ali@475: return retval; ali@475: } ali@475: ali@476: RAZOR_EXPORT int ali@476: razor_uri_unlink(const char *uri, struct razor_error **error) ali@475: { ali@475: int retval; ali@475: char *path; ali@475: struct razor_uri ru; ali@475: struct razor_uri_vtable_entry *entry; ali@475: struct razor_error *tmp_error = NULL; ali@475: ali@475: if (razor_uri_parse(&ru, uri, error)) ali@475: return -1; ali@475: ali@475: path = razor_path_from_parsed_uri(&ru, &tmp_error); ali@475: ali@475: if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI)) ali@475: razor_error_free(tmp_error); ali@475: else if (!path) { ali@475: razor_propagate_error(error, tmp_error, NULL); ali@475: razor_uri_destroy(&ru); ali@475: return -1; ali@475: } ali@475: ali@475: if (path) { ali@475: razor_uri_destroy(&ru); ali@475: retval = razor_file_unlink(path, error); ali@475: free(path); ali@475: } else { ali@475: entry = razor_uri_get_vtable_entry(ru.scheme); ali@475: razor_uri_destroy(&ru); ali@475: if (!entry || !entry->vtable.unlink) { ali@475: retval = -1; ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI, ali@475: uri, "No URI handler installed"); ali@475: } else ali@475: retval = entry->vtable.unlink(uri, error); ali@475: } ali@475: ali@475: return retval; ali@475: } ali@475: ali@476: RAZOR_EXPORT int ali@476: razor_uri_open(const char *uri, int flags, mode_t mode, ali@476: struct razor_error **error) ali@475: { ali@475: int retval; ali@475: char *path; ali@475: struct razor_uri ru; ali@475: struct razor_uri_vtable_entry *entry; ali@475: struct razor_error *tmp_error = NULL; ali@475: ali@475: if (razor_uri_parse(&ru, uri, error)) ali@475: return -1; ali@475: ali@475: path = razor_path_from_parsed_uri(&ru, &tmp_error); ali@475: ali@475: if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI)) ali@475: razor_error_free(tmp_error); ali@475: else if (!path) { ali@475: razor_propagate_error(error, tmp_error, NULL); ali@475: razor_uri_destroy(&ru); ali@475: return -1; ali@475: } ali@475: ali@475: if (path) { ali@475: razor_uri_destroy(&ru); ali@475: retval = razor_file_open(path, flags, mode, error); ali@475: free(path); ali@475: } else { ali@475: entry = razor_uri_get_vtable_entry(ru.scheme); ali@475: razor_uri_destroy(&ru); ali@475: if (!entry || !entry->vtable.open) { ali@475: retval = -1; ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI, ali@475: uri, "No URI handler installed"); ali@475: } else ali@475: retval = entry->vtable.open(uri, flags, mode, error); ali@475: } ali@475: ali@475: return retval; ali@475: } ali@475: ali@476: RAZOR_EXPORT int ali@476: razor_uri_move(const char *src_uri, const char *dst_uri, ali@476: struct razor_error **error) ali@475: { ali@475: int retval; ali@475: char *src_path, *dst_path; ali@475: struct razor_uri src_ru, dst_ru; ali@475: struct razor_uri_vtable_entry *entry; ali@475: struct razor_error *tmp_error = NULL; ali@475: ali@475: if (razor_uri_parse(&src_ru, src_uri, error) || ali@475: razor_uri_parse(&dst_ru, dst_uri, error)) ali@475: return -1; ali@475: ali@475: src_path = razor_path_from_parsed_uri(&src_ru, &tmp_error); ali@475: ali@475: if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI)) ali@475: razor_error_free(tmp_error); ali@475: else if (!src_path) { ali@475: razor_propagate_error(error, tmp_error, NULL); ali@475: razor_uri_destroy(&src_ru); ali@475: return -1; ali@475: } ali@475: ali@475: dst_path = razor_path_from_parsed_uri(&dst_ru, &tmp_error); ali@475: ali@475: if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI)) ali@475: razor_error_free(tmp_error); ali@475: else if (!dst_path) { ali@475: razor_propagate_error(error, tmp_error, NULL); ali@475: razor_uri_destroy(&dst_ru); ali@475: razor_uri_destroy(&src_ru); ali@475: free(src_path); ali@475: return -1; ali@475: } ali@475: ali@475: if (src_path && dst_path) ali@475: retval = razor_file_move(src_path, dst_path, error); ali@475: else { ali@475: if (!strcmp(src_ru.scheme, dst_ru.scheme)) ali@475: entry = razor_uri_get_vtable_entry(src_ru.scheme); ali@475: else ali@475: entry = NULL; ali@475: if (entry && entry->vtable.move) ali@475: retval = entry->vtable.move(src_uri, dst_uri, error); ali@475: else if (strcmp(src_ru.scheme, dst_ru.scheme)) { ali@475: retval = -1; ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI, ali@475: dst_uri, "Cross-scheme URI move"); ali@475: } else { ali@475: retval = -1; ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI, ali@475: src_path ? dst_uri : src_uri, ali@475: "No URI handler installed"); ali@475: } ali@475: } ali@475: ali@475: razor_uri_destroy(&src_ru); ali@475: razor_uri_destroy(&dst_ru); ali@475: free(src_path); ali@475: free(dst_path); ali@475: ali@475: return retval; ali@475: } ali@475: ali@476: RAZOR_EXPORT void * ali@476: razor_uri_get_contents(const char *uri, size_t *length, int private, ali@476: struct razor_error **error) ali@475: { ali@475: void *retval; ali@475: char *path; ali@475: struct razor_uri ru; ali@475: struct razor_uri_vtable_entry *entry; ali@475: struct razor_error *tmp_error = NULL; ali@475: ali@475: if (razor_uri_parse(&ru, uri, error)) ali@475: return NULL; ali@475: ali@475: path = razor_path_from_parsed_uri(&ru, &tmp_error); ali@475: ali@475: if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI)) ali@475: razor_error_free(tmp_error); ali@475: else if (!path) { ali@475: razor_propagate_error(error, tmp_error, NULL); ali@475: razor_uri_destroy(&ru); ali@475: return NULL; ali@475: } ali@475: ali@475: if (path) { ali@475: razor_uri_destroy(&ru); ali@475: retval = razor_file_get_contents(path, length, private, error); ali@475: free(path); ali@475: } else { ali@475: entry = razor_uri_get_vtable_entry(ru.scheme); ali@475: razor_uri_destroy(&ru); ali@475: if (!entry || !entry->vtable.get_contents) { ali@475: retval = NULL; ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI, ali@475: uri, "No URI handler installed"); ali@475: } else { ali@475: retval = entry->vtable.get_contents(uri, length, ali@475: private, error); ali@475: if (retval) ali@475: ptr_array_add(&entry->open_files, retval); ali@475: } ali@475: } ali@475: ali@475: return retval; ali@475: } ali@475: ali@476: RAZOR_EXPORT int razor_uri_free_contents(void *addr, size_t length) ali@475: { ali@475: int retval, of; ali@475: struct razor_uri_vtable_entry *entry, *eend; ali@475: ali@475: if (!addr) ali@475: return 0; ali@475: ali@475: retval = razor_file_free_contents(addr, length); ali@475: ali@475: if (retval != -2) ali@475: return retval; ali@475: ali@475: eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size; ali@475: for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) { ali@475: of = ptr_array_find(&entry->open_files, addr); ali@475: if (of >= 0) { ali@475: if (entry->vtable.free_contents) ali@475: retval = entry->vtable.free_contents(addr, ali@475: length); ali@475: ptr_array_remove_index(&entry->open_files, of); ali@475: break; ali@475: } ali@475: } ali@475: ali@475: return retval; ali@475: } ali@475: ali@476: RAZOR_EXPORT int ali@476: razor_uri_is_directory(const char *uri, struct razor_error **error) ali@475: { ali@475: int retval; ali@475: char *path; ali@475: struct razor_uri ru; ali@475: struct razor_uri_vtable_entry *entry; ali@475: struct razor_error *tmp_error = NULL; ali@475: ali@475: if (razor_uri_parse(&ru, uri, error)) ali@475: return -1; ali@475: ali@475: path = razor_path_from_parsed_uri(&ru, &tmp_error); ali@475: ali@475: if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI)) ali@475: razor_error_free(tmp_error); ali@475: else if (!path) { ali@475: razor_propagate_error(error, tmp_error, NULL); ali@475: razor_uri_destroy(&ru); ali@475: return -1; ali@475: } ali@475: ali@475: if (path) { ali@475: razor_uri_destroy(&ru); ali@475: retval = razor_file_is_directory(path, error); ali@475: free(path); ali@475: } else { ali@475: entry = razor_uri_get_vtable_entry(ru.scheme); ali@475: razor_uri_destroy(&ru); ali@475: if (!entry || !entry->vtable.is_directory) { ali@475: retval = -1; ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI, ali@475: uri, "No URI handler installed"); ali@475: } else ali@475: retval = entry->vtable.is_directory(uri, error); ali@475: } ali@475: ali@475: return retval; ali@475: } ali@475: ali@476: RAZOR_EXPORT char * ali@476: razor_uri_mkdtemp_near(const char *uri, const char *template, ali@476: struct razor_error **error) ali@475: { ali@475: char *retval, *s; ali@475: char *path; ali@475: struct razor_uri ru; ali@475: struct razor_uri_vtable_entry *entry; ali@475: struct razor_error *tmp_error = NULL; ali@475: ali@475: if (razor_uri_parse(&ru, uri, error)) ali@475: return NULL; ali@475: ali@475: path = razor_path_from_parsed_uri(&ru, &tmp_error); ali@475: ali@475: if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI)) ali@475: razor_error_free(tmp_error); ali@475: else if (!path) { ali@475: razor_propagate_error(error, tmp_error, NULL); ali@475: razor_uri_destroy(&ru); ali@475: return NULL; ali@475: } ali@475: ali@475: if (path) { ali@475: razor_uri_destroy(&ru); ali@475: s = razor_file_mkdtemp_near(path, template, error); ali@479: if (s) { ali@479: retval = razor_path_to_uri(s); ali@479: free(s); ali@479: } else ali@479: retval = NULL; ali@475: free(path); ali@475: } else { ali@475: entry = razor_uri_get_vtable_entry(ru.scheme); ali@475: razor_uri_destroy(&ru); ali@475: if (!entry || !entry->vtable.mkdtemp_near) { ali@475: retval = NULL; ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI, ali@475: uri, "No URI handler installed"); ali@475: } else ali@475: retval = entry->vtable.mkdtemp_near(uri, template, ali@475: error); ali@475: } ali@475: ali@475: return retval; ali@475: } ali@475: ali@476: RAZOR_EXPORT void * ali@476: razor_uri_opendir(const char *uri, struct razor_error **error) ali@475: { ali@475: void *retval; ali@475: char *path; ali@475: struct razor_uri ru; ali@475: struct razor_uri_vtable_entry *entry; ali@475: struct razor_error *tmp_error = NULL; ali@475: ali@475: if (razor_uri_parse(&ru, uri, error)) ali@475: return NULL; ali@475: ali@475: path = razor_path_from_parsed_uri(&ru, &tmp_error); ali@475: ali@475: if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI)) ali@475: razor_error_free(tmp_error); ali@475: else if (!path) { ali@475: razor_propagate_error(error, tmp_error, NULL); ali@475: razor_uri_destroy(&ru); ali@475: return NULL; ali@475: } ali@475: ali@475: if (path) { ali@475: razor_uri_destroy(&ru); ali@475: retval = razor_file_opendir(path, error); ali@475: free(path); ali@475: } else { ali@475: entry = razor_uri_get_vtable_entry(ru.scheme); ali@475: razor_uri_destroy(&ru); ali@475: if (!entry || !entry->vtable.opendir) { ali@475: retval = NULL; ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_UNSUPPORTED_URI, ali@475: uri, "No URI handler installed"); ali@475: } else { ali@475: retval = entry->vtable.opendir(uri, error); ali@475: if (retval) ali@475: ptr_array_add(&entry->open_directories, retval); ali@475: } ali@475: } ali@475: ali@475: return retval; ali@475: } ali@475: ali@476: RAZOR_EXPORT char *razor_uri_readdir(void *dir, struct razor_error **error) ali@475: { ali@475: int od; ali@475: char *retval; ali@475: struct razor_uri_vtable_entry *entry, *eend; ali@475: ali@475: retval = razor_file_readdir(dir, error); ali@475: ali@475: if (retval != (char *)-1) ali@475: return retval; ali@475: ali@475: eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size; ali@475: for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) { ali@475: od = ptr_array_find(&entry->open_directories, dir); ali@475: if (od >= 0) { ali@475: if (entry->vtable.readdir) ali@475: retval = entry->vtable.readdir(dir, error); ali@475: break; ali@475: } ali@475: } ali@475: ali@475: if (retval == (char *)-1) { ali@475: retval = NULL; ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_FAILED, NULL, ali@475: "Invalid directory handle"); ali@475: } ali@475: ali@475: return retval; ali@475: } ali@475: ali@476: RAZOR_EXPORT int razor_uri_closedir(void *dir, struct razor_error **error) ali@475: { ali@475: int od; ali@475: int retval; ali@475: struct razor_uri_vtable_entry *entry, *eend; ali@475: ali@475: retval = razor_file_closedir(dir, error); ali@475: ali@475: if (retval != -2) ali@475: return retval; ali@475: ali@475: eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size; ali@475: for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) { ali@475: od = ptr_array_find(&entry->open_directories, dir); ali@475: if (od >= 0) { ali@475: if (entry->vtable.closedir) ali@475: retval = entry->vtable.closedir(dir, error); ali@475: break; ali@475: } ali@475: } ali@475: ali@475: if (retval == -2) ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_FAILED, NULL, ali@475: "Invalid directory handle"); ali@475: ali@475: return retval; ali@475: } ali@475: ali@475: RAZOR_EXPORT int razor_uri_set_vtable(const char *scheme, ali@475: struct razor_uri_vtable *vtable, struct razor_error **error) ali@475: { ali@475: struct razor_uri_vtable_entry *entry, *eend; ali@475: ali@475: if (!strcmp0(scheme, "file")) { ali@475: /* ali@475: * There's no fundamental reason why we couldn't support ali@475: * this, but it's a lot of work without any obvious need. ali@475: */ ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_FAILED, scheme, ali@475: "Can't override file scheme handler"); ali@475: return -1; ali@475: } ali@475: ali@475: if (vtable->structure_size < sizeof(struct razor_uri_vtable)) { ali@475: /* ali@475: * A future version of the API might add vfuncs to the ali@475: * table (which we ignore), but won't remove any. ali@475: */ ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_FAILED, scheme, ali@475: "Invalid vtable structure size"); ali@475: return -1; ali@475: } ali@475: ali@475: eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size; ali@475: for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) { ali@475: if (!strcmp0(entry->scheme, scheme)) ali@475: break; ali@475: } ali@475: ali@475: if (entry == eend) { ali@475: if (!vtable) ali@475: return 0; ali@475: entry = array_add(&razor_uri_vtable_entries, sizeof *entry); ali@475: entry->scheme = scheme ? strdup(scheme) : NULL; ali@475: array_init(&entry->open_files); ali@475: array_init(&entry->open_directories); ali@475: } else if (entry->open_files.size || entry->open_directories.size) { ali@475: razor_set_error(error, RAZOR_GENERAL_ERROR, ali@475: RAZOR_GENERAL_ERROR_FAILED, scheme, ali@475: "URI handler busy"); ali@475: return -1; ali@475: } ali@475: ali@475: if (vtable) { ali@475: entry->vtable = *vtable; ali@475: entry->vtable.structure_size = sizeof(struct razor_uri_vtable); ali@475: } else { ali@475: free(entry->scheme); ali@475: if (entry + 1 < eend) ali@475: memmove(entry, entry + 1, eend - (entry + 1)); ali@475: array_set_size(&razor_uri_vtable_entries, ali@475: razor_uri_vtable_entries.size - sizeof *entry); ali@475: } ali@475: ali@475: return 0; ali@475: }