1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/librazor/uri-io.c Mon Jul 04 10:48:18 2016 +0100
1.3 @@ -0,0 +1,1177 @@
1.4 +/*
1.5 + * Copyright (C) 2008 Kristian Høgsberg <krh@redhat.com>
1.6 + * Copyright (C) 2008 Red Hat, Inc
1.7 + * Copyright (C) 2009, 2011, 2012, 2014, 2016 J. Ali Harlow <ali@juiblex.co.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or modify
1.10 + * it under the terms of the GNU General Public License as published by
1.11 + * the Free Software Foundation; either version 2 of the License, or
1.12 + * (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License along
1.20 + * with this program; if not, write to the Free Software Foundation, Inc.,
1.21 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1.22 + */
1.23 +
1.24 +#include "config.h"
1.25 +
1.26 +#include <limits.h>
1.27 +#include <string.h>
1.28 +#include <sys/types.h>
1.29 +#include <sys/stat.h>
1.30 +#include <stdlib.h>
1.31 +#include <stdio.h>
1.32 +#include <stdint.h>
1.33 +#include <errno.h>
1.34 +#include <unistd.h>
1.35 +#include <fcntl.h>
1.36 +#include <dirent.h>
1.37 +#ifdef MSWIN_API
1.38 +#include <windows.h>
1.39 +#include <direct.h>
1.40 +#endif
1.41 +#if HAVE_SYS_MMAN_H
1.42 +#include <sys/mman.h>
1.43 +#endif
1.44 +#include <assert.h>
1.45 +
1.46 +#include "razor.h"
1.47 +#include "types/types.h"
1.48 +#include "razor-internal.h"
1.49 +
1.50 +#ifndef O_BINARY
1.51 +#define O_BINARY 0
1.52 +#endif
1.53 +
1.54 +#define strcmp0(s1, s2) ((s1) && (s2) ? strcmp(s1, s2) : \
1.55 + (s1) ? 1 : (s2) ? -1 : 0)
1.56 +
1.57 +#define OPEN_FILE_USED (1U<<0)
1.58 +#define OPEN_FILE_MMAPPED (1U<<1)
1.59 +
1.60 +struct open_file {
1.61 + void *addr;
1.62 + size_t length;
1.63 + uint32_t flags;
1.64 +};
1.65 +
1.66 +struct array open_files = { 0, };
1.67 +
1.68 +void *razor_file_get_contents(const char *filename, size_t *length, int private,
1.69 + struct razor_error **error)
1.70 +{
1.71 + int fd;
1.72 + struct stat st;
1.73 + void *addr = NULL;
1.74 + size_t nb;
1.75 + ssize_t res;
1.76 + struct open_file *of, *ofend;
1.77 +
1.78 + fd = open(filename, O_RDONLY | O_BINARY);
1.79 + if (fd < 0) {
1.80 + razor_set_error_posix(error, filename);
1.81 + return NULL;
1.82 + }
1.83 +
1.84 + if (fstat(fd, &st) < 0) {
1.85 + razor_set_error_posix(error, filename);
1.86 + close(fd);
1.87 + return NULL;
1.88 + }
1.89 +
1.90 + *length = st.st_size;
1.91 +
1.92 + ofend = open_files.data + open_files.size;
1.93 + for (of = open_files.data; of < ofend; of++)
1.94 + if (!(of->flags & OPEN_FILE_USED))
1.95 + break;
1.96 + if (of == ofend) {
1.97 + of = array_add(&open_files, sizeof *of);
1.98 + of->flags = 0;
1.99 + }
1.100 +
1.101 +#if HAVE_SYS_MMAN_H
1.102 + if (!private) {
1.103 + addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
1.104 + if (addr == MAP_FAILED)
1.105 + addr = NULL;
1.106 + else
1.107 + of->flags = OPEN_FILE_USED | OPEN_FILE_MMAPPED;
1.108 + }
1.109 +#endif /* HAVE_SYS_MMAN_H */
1.110 + if (!addr) {
1.111 + addr = malloc(st.st_size);
1.112 + if (addr) {
1.113 + of->flags = OPEN_FILE_USED;
1.114 + nb = 0;
1.115 + while(nb < st.st_size) {
1.116 + res = read(fd, addr + nb, st.st_size - nb);
1.117 + if (res <= 0) {
1.118 + razor_set_error_posix(error, filename);
1.119 + free(addr);
1.120 + addr = NULL;
1.121 + break;
1.122 + }
1.123 + nb += res;
1.124 + }
1.125 + } else
1.126 + razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
1.127 + "Not enough memory");
1.128 + }
1.129 + close(fd);
1.130 +
1.131 + of->addr = addr;
1.132 + of->length = st.st_size;
1.133 +
1.134 + return addr;
1.135 +}
1.136 +
1.137 +int razor_file_free_contents(void *addr, size_t length)
1.138 +{
1.139 +#if HAVE_SYS_MMAN_H
1.140 + int mmapped;
1.141 +#endif
1.142 + struct open_file *of, *ofend;
1.143 +
1.144 + ofend = open_files.data + open_files.size;
1.145 + for (of = open_files.data; of < ofend; of++)
1.146 + if ((of->flags & OPEN_FILE_USED) && of->addr == addr)
1.147 + break;
1.148 +
1.149 + if (of == ofend)
1.150 + return -2;
1.151 +
1.152 + length = of->length;
1.153 +#if HAVE_SYS_MMAN_H
1.154 + mmapped = of->flags & OPEN_FILE_MMAPPED;
1.155 +#endif
1.156 + of->flags &= ~OPEN_FILE_USED;
1.157 +
1.158 + for (of = open_files.data; of < ofend; of++)
1.159 + if (of->flags & OPEN_FILE_USED)
1.160 + break;
1.161 +
1.162 + if (of == ofend) {
1.163 + array_release(&open_files);
1.164 + array_init(&open_files);
1.165 + }
1.166 +
1.167 +#if HAVE_SYS_MMAN_H
1.168 + if (mmapped)
1.169 + return munmap(addr, length);
1.170 +#endif
1.171 +
1.172 + free(addr);
1.173 + return 0;
1.174 +}
1.175 +
1.176 +int razor_file_mkdir(const char *path, mode_t mode, struct razor_error **error)
1.177 +{
1.178 + int retval, code;
1.179 + struct stat buf;
1.180 +
1.181 + retval = mkdir(path, mode);
1.182 +
1.183 + if (retval) {
1.184 + /*
1.185 + * Ignore the case of a pre-existing directory and give
1.186 + * an explicit error message if there is something other
1.187 + * than a directory already at path.
1.188 + */
1.189 + code = errno;
1.190 + if (code != EEXIST || stat(path, &buf))
1.191 + razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
1.192 + strerror(code));
1.193 + else if (!S_ISDIR(buf.st_mode))
1.194 + razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
1.195 + "Not a directory");
1.196 + }
1.197 +
1.198 + return retval;
1.199 +}
1.200 +
1.201 +int razor_file_unlink(const char *path, struct razor_error **error)
1.202 +{
1.203 + int retval;
1.204 +
1.205 + retval = unlink(path);
1.206 +
1.207 + if (retval)
1.208 + razor_set_error_posix(error, path);
1.209 +
1.210 + return retval;
1.211 +}
1.212 +
1.213 +int razor_file_open(const char *path, int flags, mode_t mode,
1.214 + struct razor_error **error)
1.215 +{
1.216 + int retval;
1.217 +
1.218 + retval = open(path, flags, mode);
1.219 +
1.220 + if (retval < 0)
1.221 + razor_set_error_posix(error, path);
1.222 +
1.223 + return retval;
1.224 +}
1.225 +
1.226 +int razor_file_move(const char *path, const char *dest,
1.227 + struct razor_error **error)
1.228 +{
1.229 + int retval = 0;
1.230 +
1.231 +#ifdef MSWIN_API
1.232 + wchar_t *oldbuf, *newbuf;
1.233 + const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
1.234 +
1.235 + newbuf = razor_utf8_to_utf16(dest, -1);
1.236 + oldbuf = razor_utf8_to_utf16(path, -1);
1.237 +
1.238 + /*
1.239 + * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
1.240 + * cover every case we care about _except_ replacing an empty
1.241 + * directory with a file. Calling RemoveDirectory() will deal
1.242 + * with this case while having no effect in all other cases.
1.243 + */
1.244 + (void)RemoveDirectoryW(newbuf);
1.245 +
1.246 + if (!MoveFileExW(oldbuf, newbuf, flags)) {
1.247 + razor_set_error_mswin(error, newbuf, GetLastError());
1.248 + retval = -1;
1.249 + }
1.250 +
1.251 + free(newbuf);
1.252 + free(oldbuf);
1.253 +#else
1.254 + int code;
1.255 + const char *object;
1.256 +
1.257 + if (rename(path, dest)) {
1.258 + if (error) {
1.259 + code = errno;
1.260 + if (access(path, F_OK) < 0)
1.261 + object = path;
1.262 + else
1.263 + object = dest;
1.264 + razor_set_error(error, RAZOR_POSIX_ERROR, code, object,
1.265 + strerror(code));
1.266 + }
1.267 + retval = -1;
1.268 + }
1.269 +#endif
1.270 +
1.271 + return retval;
1.272 +}
1.273 +
1.274 +#ifndef MSWIN_API
1.275 +static char *absolute_path(const char *path)
1.276 +{
1.277 + int len;
1.278 + char *result, *subpath, *p, *s, *t;
1.279 +
1.280 + result = realpath(path, NULL);
1.281 +
1.282 + if (!result && errno == ENOENT) {
1.283 + p = strdup(path);
1.284 + s = strrchr(p, '/');
1.285 +
1.286 + while (s) {
1.287 + if (s == p) {
1.288 + result = strdup("/");
1.289 + break;
1.290 + }
1.291 +
1.292 + *s = '\0';
1.293 + subpath = realpath(p, NULL);
1.294 +
1.295 + if (subpath) {
1.296 + *s = '/';
1.297 + len = strlen(subpath);
1.298 + result = malloc(len + strlen(s) + 1);
1.299 + memcpy(result, subpath, len);
1.300 + strcpy(result + len, s);
1.301 + free(subpath);
1.302 + break;
1.303 + } else if (errno != ENOENT)
1.304 + break;
1.305 +
1.306 + t = strrchr(p, '/');
1.307 + *s = '/';
1.308 + s = t;
1.309 + }
1.310 +
1.311 + if (!s)
1.312 + result = realpath(".", NULL);
1.313 +
1.314 + free(p);
1.315 + }
1.316 +
1.317 + return result;
1.318 +}
1.319 +#endif
1.320 +
1.321 +int razor_file_is_directory(const char *path, struct razor_error **error)
1.322 +{
1.323 + struct stat st;
1.324 +
1.325 + if (stat(path, &st) < 0) {
1.326 + razor_set_error_posix(error, path);
1.327 + return -1;
1.328 + }
1.329 +
1.330 + return !!S_ISDIR(st.st_mode);
1.331 +}
1.332 +
1.333 +char *razor_file_mkdtemp_near(const char *path, const char *template,
1.334 + struct razor_error **error)
1.335 +{
1.336 + char *retval;
1.337 +
1.338 +#ifdef MSWIN_API
1.339 + if (path[0]=='\\' && path[1]=='\\' && path[2] && path[2]!='\\'
1.340 + && strchr(path+3,'\\')) {
1.341 + /* We have a UNC path: \\servername\sharename... */
1.342 + const char *sharename, *root;
1.343 + int disklen;
1.344 +
1.345 + sharename = strchr(path+3,'\\')+1;
1.346 + root = strchr(sharename,'\\');
1.347 + if (root)
1.348 + disklen = root - path;
1.349 + else
1.350 + disklen = strlen(path);
1.351 +
1.352 + retval = malloc(disklen + 1 + strlen(template) + 1);
1.353 + memcpy(retval, path, disklen);
1.354 + retval[disklen] = '\\';
1.355 + strcpy(retval + disklen + 1, template);
1.356 + } else if ((*path>='A' && *path<='Z' || *path>='a' && *path<='z') &&
1.357 + path[1]==':') {
1.358 + retval = malloc(3 + strlen(template) + 1);
1.359 + retval[0] = path[0];
1.360 + retval[1] = ':';
1.361 + retval[2] = '\\';
1.362 + strcpy(retval + 3, template);
1.363 + } else {
1.364 + DWORD n;
1.365 + wchar_t *buf;
1.366 + char *dir;
1.367 +
1.368 + n = GetCurrentDirectoryW(0, NULL);
1.369 + buf = malloc(n * sizeof(wchar_t));
1.370 +
1.371 + if (GetCurrentDirectoryW(n, buf)) {
1.372 + dir = razor_utf16_to_utf8(buf, n - 1);
1.373 + free(buf);
1.374 + retval = razor_file_mkdtemp_near(dir, template, error);
1.375 + free(dir);
1.376 + return retval;
1.377 + } else {
1.378 + retval = malloc(3 + strlen(template) + 1);
1.379 + retval[0] = 'C';
1.380 + retval[1] = ':';
1.381 + retval[2] = '\\';
1.382 + strcpy(retval + 3, template);
1.383 + }
1.384 +
1.385 + free(buf);
1.386 + }
1.387 +#else
1.388 + /*
1.389 + * Find the mount point (assuming we can write to the
1.390 + * whole filesystem). Otherwise stop at the first
1.391 + * unwritable directory and take one step back.
1.392 + */
1.393 + char *s, *abspath, saved;
1.394 + int len, can_step_back = 0;
1.395 + dev_t filesystem;
1.396 + struct stat buf;
1.397 +
1.398 + abspath = absolute_path(path);
1.399 + if (!abspath) {
1.400 + razor_set_error_posix(error, path);
1.401 + return NULL;
1.402 + }
1.403 +
1.404 + if (stat(abspath, &buf) < 0) {
1.405 + if (errno == ENOENT)
1.406 + filesystem = 0;
1.407 + else {
1.408 + razor_set_error_posix(error, abspath);
1.409 + free(abspath);
1.410 + return NULL;
1.411 + }
1.412 + } else
1.413 + filesystem = buf.st_dev;
1.414 +
1.415 + len = strlen(abspath);
1.416 + while(len > 1 && (s = strrchr(abspath, '/'))) {
1.417 + if (s == abspath) {
1.418 + saved = s[1];
1.419 + s[1] = '\0';
1.420 + len = s + 1 - abspath;
1.421 + } else {
1.422 + s[0] = '\0';
1.423 + len = s - abspath;
1.424 + }
1.425 +
1.426 + if (stat(abspath, &buf) < 0) {
1.427 + if (errno == ENOENT)
1.428 + continue;
1.429 + else {
1.430 + razor_set_error_posix(error, abspath);
1.431 + free(abspath);
1.432 + return NULL;
1.433 + }
1.434 + } else if (!filesystem)
1.435 + filesystem = buf.st_dev;
1.436 +
1.437 + if (buf.st_dev != filesystem || access(abspath, W_OK)) {
1.438 + if (can_step_back) {
1.439 + if (s == abspath)
1.440 + s[1] = saved;
1.441 + else
1.442 + s[0] = '/';
1.443 + }
1.444 + len = strlen(abspath);
1.445 + break;
1.446 + } else
1.447 + can_step_back = 1;
1.448 + }
1.449 +
1.450 + if (len == 1)
1.451 + len = 0; /* Avoid an unslightly double slash. */
1.452 + retval = malloc(len + 1 + strlen(template) + 1);
1.453 + memcpy(retval, abspath, len);
1.454 + retval[len] = '/';
1.455 + strcpy(retval + len + 1, template);
1.456 +
1.457 + free(abspath);
1.458 +#endif
1.459 +
1.460 + if (!mkdtemp(retval)) {
1.461 + int err = errno;
1.462 +
1.463 +#ifdef EACCES
1.464 + if (err == EACCES) {
1.465 + char *s = strdup(template);
1.466 +
1.467 +#ifndef MSWIN_API
1.468 + if (stat(".", &buf) < 0) {
1.469 + razor_set_error_posix(error, ".");
1.470 + free(s);
1.471 + free(retval);
1.472 + return NULL;
1.473 + }
1.474 + if (buf.st_dev != filesystem)
1.475 + /*
1.476 + * Don't use a different filesystem. It will
1.477 + * only fail later on (in rename) and cause
1.478 + * an unhelpful error message (EXDEV).
1.479 + */
1.480 + free(s);
1.481 + else
1.482 +#endif
1.483 + if (mkdtemp(s)) {
1.484 + free(retval);
1.485 + retval = s;
1.486 + return retval;
1.487 + } else
1.488 + free(s);
1.489 + }
1.490 +#endif
1.491 +
1.492 + razor_set_error(error, RAZOR_POSIX_ERROR, err, retval,
1.493 + strerror(err));
1.494 +
1.495 + free(retval);
1.496 + retval = NULL;
1.497 + }
1.498 +
1.499 + return retval;
1.500 +}
1.501 +
1.502 +struct open_dir {
1.503 + uint32_t flags;
1.504 +#ifdef MSWIN_API
1.505 + _WDIR *dp;
1.506 + wchar_t *path2;
1.507 +#else
1.508 + DIR *dp;
1.509 + char *path;
1.510 +#endif
1.511 +};
1.512 +
1.513 +#define OPEN_DIR_USED (1U<<0)
1.514 +
1.515 +struct array open_dirs = { 0, };
1.516 +
1.517 +void *razor_file_opendir(const char *path, struct razor_error **error)
1.518 +{
1.519 + struct open_dir *od, *odend;
1.520 +
1.521 + odend = open_dirs.data + open_dirs.size;
1.522 + for (od = open_dirs.data; od < odend; od++)
1.523 + if (!(od->flags & OPEN_DIR_USED))
1.524 + break;
1.525 + if (od == odend) {
1.526 + od = array_add(&open_dirs, sizeof *od);
1.527 + od->flags = 0;
1.528 + }
1.529 +
1.530 +#ifdef MSWIN_API
1.531 + od->path2 = razor_utf8_to_utf16(path, -1);
1.532 + od->dp = _wopendir(od->path2);
1.533 +#else
1.534 + od->path = strdup(path);
1.535 + od->dp = opendir(od->path);
1.536 +#endif
1.537 +
1.538 + if (!od->dp) {
1.539 +#ifdef MSWIN_API
1.540 + razor_set_error_mswin(error, od->path2, GetLastError());
1.541 + free(od->path2);
1.542 +#else
1.543 + razor_set_error_posix(error, od->path);
1.544 + free(od->path);
1.545 +#endif
1.546 + return NULL;
1.547 + }
1.548 +
1.549 + od->flags = OPEN_DIR_USED;
1.550 + return od;
1.551 +}
1.552 +
1.553 +char *razor_file_readdir(void *dp, struct razor_error **error)
1.554 +{
1.555 + struct open_dir *od = dp, *odend;
1.556 +#ifdef MSWIN_API
1.557 + struct _wdirent *dirp;
1.558 + char *path;
1.559 +#else
1.560 + struct dirent *dirp;
1.561 +#endif
1.562 +
1.563 + odend = open_dirs.data + open_dirs.size;
1.564 + if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
1.565 + return (char *)-1;
1.566 +
1.567 + errno = 0;
1.568 +
1.569 +#ifdef MSWIN_API
1.570 + while((dirp = _wreaddir(od->dp))) {
1.571 + path = razor_utf16_to_utf8(dirp->d_name, -1);
1.572 + if (strcmp(path, ".") && strcmp(path, ".."))
1.573 + return path;
1.574 + else
1.575 + free(path);
1.576 + }
1.577 +#else
1.578 + while((dirp = readdir(od->dp)))
1.579 + if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, ".."))
1.580 + return strdup(dirp->d_name);
1.581 +#endif
1.582 +
1.583 + if (errno) {
1.584 +#ifdef MSWIN_API
1.585 + razor_set_error_mswin(error, od->path2, GetLastError());
1.586 +#else
1.587 + razor_set_error_posix(error, od->path);
1.588 +#endif
1.589 + }
1.590 +
1.591 + return NULL;
1.592 +}
1.593 +
1.594 +int razor_file_closedir(void *dp, struct razor_error **error)
1.595 +{
1.596 + struct open_dir *od = dp, *odend;
1.597 + int retval;
1.598 +
1.599 + odend = open_dirs.data + open_dirs.size;
1.600 + if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
1.601 + return -2;
1.602 +
1.603 +#ifdef MSWIN_API
1.604 + /*
1.605 + * I can't find documentation to state that _wclosedir()
1.606 + * returns -1 on failure, so be paranoid.
1.607 + */
1.608 + retval = _wclosedir(od->dp) ? -1 : 0;
1.609 +#else
1.610 + retval = closedir(od->dp);
1.611 +#endif
1.612 +
1.613 + if (retval) {
1.614 +#ifdef MSWIN_API
1.615 + razor_set_error_mswin(error, od->path2, GetLastError());
1.616 +#else
1.617 + razor_set_error_posix(error, od->path);
1.618 +#endif
1.619 + }
1.620 +
1.621 +#ifdef MSWIN_API
1.622 + free(od->path2);
1.623 +#else
1.624 + free(od->path);
1.625 +#endif
1.626 +
1.627 + od->flags &= ~OPEN_DIR_USED;
1.628 +
1.629 + for (od = open_dirs.data; od < odend; od++)
1.630 + if (od->flags & OPEN_DIR_USED)
1.631 + break;
1.632 +
1.633 + if (od == odend) {
1.634 + array_release(&open_dirs);
1.635 + array_init(&open_dirs);
1.636 + }
1.637 +
1.638 + return retval;
1.639 +}
1.640 +
1.641 +struct razor_uri_vtable_entry {
1.642 + struct razor_uri_vtable vtable;
1.643 + char *scheme;
1.644 + struct array open_files, open_directories;
1.645 +};
1.646 +
1.647 +static struct array razor_uri_vtable_entries;
1.648 +
1.649 +static struct razor_uri_vtable_entry *
1.650 + razor_uri_get_vtable_entry(const char *scheme)
1.651 +{
1.652 + struct razor_uri_vtable_entry *entry, *eend, *fallback = NULL;
1.653 +
1.654 + eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1.655 + for(entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1.656 + if (!strcmp0(entry->scheme, scheme))
1.657 + return entry;
1.658 + else if (!entry->scheme)
1.659 + fallback = entry;
1.660 + }
1.661 +
1.662 + return fallback;
1.663 +}
1.664 +
1.665 +int razor_uri_mkdir(const char *uri, mode_t mode, struct razor_error **error)
1.666 +{
1.667 + int retval;
1.668 + char *path;
1.669 + struct razor_uri ru;
1.670 + struct razor_uri_vtable_entry *entry;
1.671 + struct razor_error *tmp_error = NULL;
1.672 +
1.673 + if (razor_uri_parse(&ru, uri, error))
1.674 + return -1;
1.675 +
1.676 + path = razor_path_from_parsed_uri(&ru, &tmp_error);
1.677 +
1.678 + if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1.679 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1.680 + razor_error_free(tmp_error);
1.681 + else if (!path) {
1.682 + razor_propagate_error(error, tmp_error, NULL);
1.683 + razor_uri_destroy(&ru);
1.684 + return -1;
1.685 + }
1.686 +
1.687 + if (path) {
1.688 + razor_uri_destroy(&ru);
1.689 + retval = razor_file_mkdir(path, mode, error);
1.690 + free(path);
1.691 + } else {
1.692 + entry = razor_uri_get_vtable_entry(ru.scheme);
1.693 + razor_uri_destroy(&ru);
1.694 + if (!entry || !entry->vtable.mkdir) {
1.695 + retval = -1;
1.696 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.697 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1.698 + uri, "No URI handler installed");
1.699 + } else
1.700 + retval = entry->vtable.mkdir(uri, mode, error);
1.701 + }
1.702 +
1.703 + return retval;
1.704 +}
1.705 +
1.706 +int razor_uri_unlink(const char *uri, struct razor_error **error)
1.707 +{
1.708 + int retval;
1.709 + char *path;
1.710 + struct razor_uri ru;
1.711 + struct razor_uri_vtable_entry *entry;
1.712 + struct razor_error *tmp_error = NULL;
1.713 +
1.714 + if (razor_uri_parse(&ru, uri, error))
1.715 + return -1;
1.716 +
1.717 + path = razor_path_from_parsed_uri(&ru, &tmp_error);
1.718 +
1.719 + if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1.720 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1.721 + razor_error_free(tmp_error);
1.722 + else if (!path) {
1.723 + razor_propagate_error(error, tmp_error, NULL);
1.724 + razor_uri_destroy(&ru);
1.725 + return -1;
1.726 + }
1.727 +
1.728 + if (path) {
1.729 + razor_uri_destroy(&ru);
1.730 + retval = razor_file_unlink(path, error);
1.731 + free(path);
1.732 + } else {
1.733 + entry = razor_uri_get_vtable_entry(ru.scheme);
1.734 + razor_uri_destroy(&ru);
1.735 + if (!entry || !entry->vtable.unlink) {
1.736 + retval = -1;
1.737 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.738 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1.739 + uri, "No URI handler installed");
1.740 + } else
1.741 + retval = entry->vtable.unlink(uri, error);
1.742 + }
1.743 +
1.744 + return retval;
1.745 +}
1.746 +
1.747 +int razor_uri_open(const char *uri, int flags, mode_t mode,
1.748 + struct razor_error **error)
1.749 +{
1.750 + int retval;
1.751 + char *path;
1.752 + struct razor_uri ru;
1.753 + struct razor_uri_vtable_entry *entry;
1.754 + struct razor_error *tmp_error = NULL;
1.755 +
1.756 + if (razor_uri_parse(&ru, uri, error))
1.757 + return -1;
1.758 +
1.759 + path = razor_path_from_parsed_uri(&ru, &tmp_error);
1.760 +
1.761 + if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1.762 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1.763 + razor_error_free(tmp_error);
1.764 + else if (!path) {
1.765 + razor_propagate_error(error, tmp_error, NULL);
1.766 + razor_uri_destroy(&ru);
1.767 + return -1;
1.768 + }
1.769 +
1.770 + if (path) {
1.771 + razor_uri_destroy(&ru);
1.772 + retval = razor_file_open(path, flags, mode, error);
1.773 + free(path);
1.774 + } else {
1.775 + entry = razor_uri_get_vtable_entry(ru.scheme);
1.776 + razor_uri_destroy(&ru);
1.777 + if (!entry || !entry->vtable.open) {
1.778 + retval = -1;
1.779 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.780 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1.781 + uri, "No URI handler installed");
1.782 + } else
1.783 + retval = entry->vtable.open(uri, flags, mode, error);
1.784 + }
1.785 +
1.786 + return retval;
1.787 +}
1.788 +
1.789 +int razor_uri_move(const char *src_uri, const char *dst_uri,
1.790 + struct razor_error **error)
1.791 +{
1.792 + int retval;
1.793 + char *src_path, *dst_path;
1.794 + struct razor_uri src_ru, dst_ru;
1.795 + struct razor_uri_vtable_entry *entry;
1.796 + struct razor_error *tmp_error = NULL;
1.797 +
1.798 + if (razor_uri_parse(&src_ru, src_uri, error) ||
1.799 + razor_uri_parse(&dst_ru, dst_uri, error))
1.800 + return -1;
1.801 +
1.802 + src_path = razor_path_from_parsed_uri(&src_ru, &tmp_error);
1.803 +
1.804 + if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1.805 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1.806 + razor_error_free(tmp_error);
1.807 + else if (!src_path) {
1.808 + razor_propagate_error(error, tmp_error, NULL);
1.809 + razor_uri_destroy(&src_ru);
1.810 + return -1;
1.811 + }
1.812 +
1.813 + dst_path = razor_path_from_parsed_uri(&dst_ru, &tmp_error);
1.814 +
1.815 + if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1.816 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1.817 + razor_error_free(tmp_error);
1.818 + else if (!dst_path) {
1.819 + razor_propagate_error(error, tmp_error, NULL);
1.820 + razor_uri_destroy(&dst_ru);
1.821 + razor_uri_destroy(&src_ru);
1.822 + free(src_path);
1.823 + return -1;
1.824 + }
1.825 +
1.826 + if (src_path && dst_path)
1.827 + retval = razor_file_move(src_path, dst_path, error);
1.828 + else {
1.829 + if (!strcmp(src_ru.scheme, dst_ru.scheme))
1.830 + entry = razor_uri_get_vtable_entry(src_ru.scheme);
1.831 + else
1.832 + entry = NULL;
1.833 + if (entry && entry->vtable.move)
1.834 + retval = entry->vtable.move(src_uri, dst_uri, error);
1.835 + else if (strcmp(src_ru.scheme, dst_ru.scheme)) {
1.836 + retval = -1;
1.837 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.838 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1.839 + dst_uri, "Cross-scheme URI move");
1.840 + } else {
1.841 + retval = -1;
1.842 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.843 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1.844 + src_path ? dst_uri : src_uri,
1.845 + "No URI handler installed");
1.846 + }
1.847 + }
1.848 +
1.849 + razor_uri_destroy(&src_ru);
1.850 + razor_uri_destroy(&dst_ru);
1.851 + free(src_path);
1.852 + free(dst_path);
1.853 +
1.854 + return retval;
1.855 +}
1.856 +
1.857 +void *razor_uri_get_contents(const char *uri, size_t *length, int private,
1.858 + struct razor_error **error)
1.859 +{
1.860 + void *retval;
1.861 + char *path;
1.862 + struct razor_uri ru;
1.863 + struct razor_uri_vtable_entry *entry;
1.864 + struct razor_error *tmp_error = NULL;
1.865 +
1.866 + if (razor_uri_parse(&ru, uri, error))
1.867 + return NULL;
1.868 +
1.869 + path = razor_path_from_parsed_uri(&ru, &tmp_error);
1.870 +
1.871 + if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1.872 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1.873 + razor_error_free(tmp_error);
1.874 + else if (!path) {
1.875 + razor_propagate_error(error, tmp_error, NULL);
1.876 + razor_uri_destroy(&ru);
1.877 + return NULL;
1.878 + }
1.879 +
1.880 + if (path) {
1.881 + razor_uri_destroy(&ru);
1.882 + retval = razor_file_get_contents(path, length, private, error);
1.883 + free(path);
1.884 + } else {
1.885 + entry = razor_uri_get_vtable_entry(ru.scheme);
1.886 + razor_uri_destroy(&ru);
1.887 + if (!entry || !entry->vtable.get_contents) {
1.888 + retval = NULL;
1.889 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.890 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1.891 + uri, "No URI handler installed");
1.892 + } else {
1.893 + retval = entry->vtable.get_contents(uri, length,
1.894 + private, error);
1.895 + if (retval)
1.896 + ptr_array_add(&entry->open_files, retval);
1.897 + }
1.898 + }
1.899 +
1.900 + return retval;
1.901 +}
1.902 +
1.903 +int razor_uri_free_contents(void *addr, size_t length)
1.904 +{
1.905 + int retval, of;
1.906 + struct razor_uri_vtable_entry *entry, *eend;
1.907 +
1.908 + if (!addr)
1.909 + return 0;
1.910 +
1.911 + retval = razor_file_free_contents(addr, length);
1.912 +
1.913 + if (retval != -2)
1.914 + return retval;
1.915 +
1.916 + eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1.917 + for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1.918 + of = ptr_array_find(&entry->open_files, addr);
1.919 + if (of >= 0) {
1.920 + if (entry->vtable.free_contents)
1.921 + retval = entry->vtable.free_contents(addr,
1.922 + length);
1.923 + ptr_array_remove_index(&entry->open_files, of);
1.924 + break;
1.925 + }
1.926 + }
1.927 +
1.928 + return retval;
1.929 +}
1.930 +
1.931 +int razor_uri_is_directory(const char *uri, struct razor_error **error)
1.932 +{
1.933 + int retval;
1.934 + char *path;
1.935 + struct razor_uri ru;
1.936 + struct razor_uri_vtable_entry *entry;
1.937 + struct razor_error *tmp_error = NULL;
1.938 +
1.939 + if (razor_uri_parse(&ru, uri, error))
1.940 + return -1;
1.941 +
1.942 + path = razor_path_from_parsed_uri(&ru, &tmp_error);
1.943 +
1.944 + if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1.945 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1.946 + razor_error_free(tmp_error);
1.947 + else if (!path) {
1.948 + razor_propagate_error(error, tmp_error, NULL);
1.949 + razor_uri_destroy(&ru);
1.950 + return -1;
1.951 + }
1.952 +
1.953 + if (path) {
1.954 + razor_uri_destroy(&ru);
1.955 + retval = razor_file_is_directory(path, error);
1.956 + free(path);
1.957 + } else {
1.958 + entry = razor_uri_get_vtable_entry(ru.scheme);
1.959 + razor_uri_destroy(&ru);
1.960 + if (!entry || !entry->vtable.is_directory) {
1.961 + retval = -1;
1.962 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.963 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1.964 + uri, "No URI handler installed");
1.965 + } else
1.966 + retval = entry->vtable.is_directory(uri, error);
1.967 + }
1.968 +
1.969 + return retval;
1.970 +}
1.971 +
1.972 +char *razor_uri_mkdtemp_near(const char *uri, const char *template,
1.973 + struct razor_error **error)
1.974 +{
1.975 + char *retval, *s;
1.976 + char *path;
1.977 + struct razor_uri ru;
1.978 + struct razor_uri_vtable_entry *entry;
1.979 + struct razor_error *tmp_error = NULL;
1.980 +
1.981 + if (razor_uri_parse(&ru, uri, error))
1.982 + return NULL;
1.983 +
1.984 + path = razor_path_from_parsed_uri(&ru, &tmp_error);
1.985 +
1.986 + if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1.987 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1.988 + razor_error_free(tmp_error);
1.989 + else if (!path) {
1.990 + razor_propagate_error(error, tmp_error, NULL);
1.991 + razor_uri_destroy(&ru);
1.992 + return NULL;
1.993 + }
1.994 +
1.995 + if (path) {
1.996 + razor_uri_destroy(&ru);
1.997 + s = razor_file_mkdtemp_near(path, template, error);
1.998 + retval = razor_path_to_uri(s);
1.999 + free(s);
1.1000 + free(path);
1.1001 + } else {
1.1002 + entry = razor_uri_get_vtable_entry(ru.scheme);
1.1003 + razor_uri_destroy(&ru);
1.1004 + if (!entry || !entry->vtable.mkdtemp_near) {
1.1005 + retval = NULL;
1.1006 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.1007 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1.1008 + uri, "No URI handler installed");
1.1009 + } else
1.1010 + retval = entry->vtable.mkdtemp_near(uri, template,
1.1011 + error);
1.1012 + }
1.1013 +
1.1014 + return retval;
1.1015 +}
1.1016 +
1.1017 +void *razor_uri_opendir(const char *uri, struct razor_error **error)
1.1018 +{
1.1019 + void *retval;
1.1020 + char *path;
1.1021 + struct razor_uri ru;
1.1022 + struct razor_uri_vtable_entry *entry;
1.1023 + struct razor_error *tmp_error = NULL;
1.1024 +
1.1025 + if (razor_uri_parse(&ru, uri, error))
1.1026 + return NULL;
1.1027 +
1.1028 + path = razor_path_from_parsed_uri(&ru, &tmp_error);
1.1029 +
1.1030 + if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1.1031 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1.1032 + razor_error_free(tmp_error);
1.1033 + else if (!path) {
1.1034 + razor_propagate_error(error, tmp_error, NULL);
1.1035 + razor_uri_destroy(&ru);
1.1036 + return NULL;
1.1037 + }
1.1038 +
1.1039 + if (path) {
1.1040 + razor_uri_destroy(&ru);
1.1041 + retval = razor_file_opendir(path, error);
1.1042 + free(path);
1.1043 + } else {
1.1044 + entry = razor_uri_get_vtable_entry(ru.scheme);
1.1045 + razor_uri_destroy(&ru);
1.1046 + if (!entry || !entry->vtable.opendir) {
1.1047 + retval = NULL;
1.1048 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.1049 + RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1.1050 + uri, "No URI handler installed");
1.1051 + } else {
1.1052 + retval = entry->vtable.opendir(uri, error);
1.1053 + if (retval)
1.1054 + ptr_array_add(&entry->open_directories, retval);
1.1055 + }
1.1056 + }
1.1057 +
1.1058 + return retval;
1.1059 +}
1.1060 +
1.1061 +char *razor_uri_readdir(void *dir, struct razor_error **error)
1.1062 +{
1.1063 + int od;
1.1064 + char *retval;
1.1065 + struct razor_uri_vtable_entry *entry, *eend;
1.1066 +
1.1067 + retval = razor_file_readdir(dir, error);
1.1068 +
1.1069 + if (retval != (char *)-1)
1.1070 + return retval;
1.1071 +
1.1072 + eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1.1073 + for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1.1074 + od = ptr_array_find(&entry->open_directories, dir);
1.1075 + if (od >= 0) {
1.1076 + if (entry->vtable.readdir)
1.1077 + retval = entry->vtable.readdir(dir, error);
1.1078 + break;
1.1079 + }
1.1080 + }
1.1081 +
1.1082 + if (retval == (char *)-1) {
1.1083 + retval = NULL;
1.1084 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.1085 + RAZOR_GENERAL_ERROR_FAILED, NULL,
1.1086 + "Invalid directory handle");
1.1087 + }
1.1088 +
1.1089 + return retval;
1.1090 +}
1.1091 +
1.1092 +int razor_uri_closedir(void *dir, struct razor_error **error)
1.1093 +{
1.1094 + int od;
1.1095 + int retval;
1.1096 + struct razor_uri_vtable_entry *entry, *eend;
1.1097 +
1.1098 + retval = razor_file_closedir(dir, error);
1.1099 +
1.1100 + if (retval != -2)
1.1101 + return retval;
1.1102 +
1.1103 + eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1.1104 + for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1.1105 + od = ptr_array_find(&entry->open_directories, dir);
1.1106 + if (od >= 0) {
1.1107 + if (entry->vtable.closedir)
1.1108 + retval = entry->vtable.closedir(dir, error);
1.1109 + break;
1.1110 + }
1.1111 + }
1.1112 +
1.1113 + if (retval == -2)
1.1114 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.1115 + RAZOR_GENERAL_ERROR_FAILED, NULL,
1.1116 + "Invalid directory handle");
1.1117 +
1.1118 + return retval;
1.1119 +}
1.1120 +
1.1121 +RAZOR_EXPORT int razor_uri_set_vtable(const char *scheme,
1.1122 + struct razor_uri_vtable *vtable, struct razor_error **error)
1.1123 +{
1.1124 + struct razor_uri_vtable_entry *entry, *eend;
1.1125 +
1.1126 + if (!strcmp0(scheme, "file")) {
1.1127 + /*
1.1128 + * There's no fundamental reason why we couldn't support
1.1129 + * this, but it's a lot of work without any obvious need.
1.1130 + */
1.1131 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.1132 + RAZOR_GENERAL_ERROR_FAILED, scheme,
1.1133 + "Can't override file scheme handler");
1.1134 + return -1;
1.1135 + }
1.1136 +
1.1137 + if (vtable->structure_size < sizeof(struct razor_uri_vtable)) {
1.1138 + /*
1.1139 + * A future version of the API might add vfuncs to the
1.1140 + * table (which we ignore), but won't remove any.
1.1141 + */
1.1142 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.1143 + RAZOR_GENERAL_ERROR_FAILED, scheme,
1.1144 + "Invalid vtable structure size");
1.1145 + return -1;
1.1146 + }
1.1147 +
1.1148 + eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1.1149 + for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1.1150 + if (!strcmp0(entry->scheme, scheme))
1.1151 + break;
1.1152 + }
1.1153 +
1.1154 + if (entry == eend) {
1.1155 + if (!vtable)
1.1156 + return 0;
1.1157 + entry = array_add(&razor_uri_vtable_entries, sizeof *entry);
1.1158 + entry->scheme = scheme ? strdup(scheme) : NULL;
1.1159 + array_init(&entry->open_files);
1.1160 + array_init(&entry->open_directories);
1.1161 + } else if (entry->open_files.size || entry->open_directories.size) {
1.1162 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.1163 + RAZOR_GENERAL_ERROR_FAILED, scheme,
1.1164 + "URI handler busy");
1.1165 + return -1;
1.1166 + }
1.1167 +
1.1168 + if (vtable) {
1.1169 + entry->vtable = *vtable;
1.1170 + entry->vtable.structure_size = sizeof(struct razor_uri_vtable);
1.1171 + } else {
1.1172 + free(entry->scheme);
1.1173 + if (entry + 1 < eend)
1.1174 + memmove(entry, entry + 1, eend - (entry + 1));
1.1175 + array_set_size(&razor_uri_vtable_entries,
1.1176 + razor_uri_vtable_entries.size - sizeof *entry);
1.1177 + }
1.1178 +
1.1179 + return 0;
1.1180 +}