2 * Copyright (C) 2008 Kristian Høgsberg <krh@redhat.com>
3 * Copyright (C) 2008 Red Hat, Inc
4 * Copyright (C) 2009, 2011, 2012, 2014, 2016 J. Ali Harlow <ali@juiblex.co.uk>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include <sys/types.h>
43 #include <archive_entry.h>
48 #include "types/types.h"
49 #include "razor-internal.h"
55 #define strcmp0(s1, s2) ((s1) && (s2) ? strcmp(s1, s2) : \
56 (s1) ? 1 : (s2) ? -1 : 0)
59 razor_archive_get_file_contents(const char *filename, int fd, const char *path,
60 size_t *length, struct razor_error **error)
70 struct archive_entry *entry;
72 a = archive_read_new();
73 archive_read_support_compression_all(a);
74 archive_read_support_format_all(a);
76 r = archive_read_open_fd(a, fd, 10240);
79 errmsg = archive_error_string(a);
80 if (!strcmp(errmsg, "Unrecognized archive format"))
81 razor_set_error(error, RAZOR_GENERAL_ERROR,
82 RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE,
85 razor_set_error(error, RAZOR_POSIX_ERROR,
86 archive_errno(a), filename, errmsg);
87 archive_read_finish(a);
92 r = archive_read_next_header(a, &entry);
95 else if (r < ARCHIVE_WARN) {
96 razor_set_error(error, RAZOR_POSIX_ERROR,
97 archive_errno(a), filename,
98 archive_error_string(a));
99 archive_read_close(a);
100 archive_read_finish(a);
105 * TODO: Unicode support. Might need to wait for libarchive v4.
107 if (!strcmp(archive_entry_pathname(entry), path)) {
108 addr = malloc(archive_entry_size(entry));
110 archive_read_close(a);
111 archive_read_finish(a);
112 razor_set_error(error, RAZOR_POSIX_ERROR,
114 "Not enough memory");
119 r = archive_read_data_block(a, &buf, &size,
121 if (r == ARCHIVE_EOF)
123 if (r < ARCHIVE_OK) {
124 razor_set_error(error, RAZOR_POSIX_ERROR,
125 archive_errno(a), path,
126 archive_error_string(a));
131 memcpy((char *)addr + offset, buf, size);
134 archive_read_close(a);
135 archive_read_finish(a);
141 archive_read_close(a);
142 archive_read_finish(a);
144 razor_set_error(error, RAZOR_POSIX_ERROR, ENOENT, path,
145 "No such file or directory in archive");
149 razor_set_error(error, RAZOR_GENERAL_ERROR,
150 RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE,
151 filename, "Archives are not supported in this build");
154 #endif /* HAVE_LIBARCHIVE */
158 razor_file_get_contents_archive(const char *filename, size_t *length,
159 struct razor_error **error)
162 char *path, *slash, *s;
164 struct razor_error *tmp_error = NULL;
166 path = strdup(filename);
167 slash = strrchr(path, '/');
171 fd = open(path, O_RDONLY | O_BINARY);
173 addr = razor_archive_get_file_contents(path, fd,
181 } else if (errno != ENOTDIR && errno != ENOENT) {
183 } else if (errno != ENOTDIR) {
186 razor_set_error_posix(error, filename);
189 s = strrchr(path, '/');
196 razor_set_error(error, RAZOR_POSIX_ERROR, ENOENT, filename,
199 razor_set_error(error, RAZOR_POSIX_ERROR, ENOTDIR, filename,
205 #define OPEN_FILE_USED (1U<<0)
206 #define OPEN_FILE_MMAPPED (1U<<1)
214 struct array open_files = { 0, };
216 void *razor_file_get_contents(const char *filename, size_t *length, int private,
217 struct razor_error **error)
224 struct open_file *of, *ofend;
226 fd = open(filename, O_RDONLY | O_BINARY);
229 if (errno != ENOTDIR && errno != ENOENT) {
231 if (errno != ENOTDIR) {
233 razor_set_error_posix(error, filename);
236 addr = razor_file_get_contents_archive(filename, &size, error);
240 if (fstat(fd, &st) < 0) {
241 razor_set_error_posix(error, filename);
248 ofend = open_files.data + open_files.size;
249 for (of = open_files.data; of < ofend; of++)
250 if (!(of->flags & OPEN_FILE_USED))
253 of = array_add(&open_files, sizeof *of);
258 if (!addr && !private) {
259 addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
260 if (addr == MAP_FAILED)
263 of->flags = OPEN_FILE_USED | OPEN_FILE_MMAPPED;
265 #endif /* HAVE_SYS_MMAN_H */
272 res = read(fd, addr + nb, size - nb);
274 razor_set_error_posix(error, filename);
282 of->flags = OPEN_FILE_USED;
284 razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
285 "Not enough memory");
300 int razor_file_free_contents(void *addr, size_t length)
305 struct open_file *of, *ofend;
307 ofend = open_files.data + open_files.size;
308 for (of = open_files.data; of < ofend; of++)
309 if ((of->flags & OPEN_FILE_USED) && of->addr == addr)
317 mmapped = of->flags & OPEN_FILE_MMAPPED;
319 of->flags &= ~OPEN_FILE_USED;
321 for (of = open_files.data; of < ofend; of++)
322 if (of->flags & OPEN_FILE_USED)
326 array_release(&open_files);
327 array_init(&open_files);
332 return munmap(addr, length);
339 int razor_file_mkdir(const char *path, mode_t mode, struct razor_error **error)
344 retval = mkdir(path, mode);
348 * Ignore the case of a pre-existing directory and give
349 * an explicit error message if there is something other
350 * than a directory already at path.
353 if (code != EEXIST || stat(path, &buf))
354 razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
356 else if (!S_ISDIR(buf.st_mode))
357 razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
364 int razor_file_unlink(const char *path, struct razor_error **error)
368 retval = unlink(path);
371 razor_set_error_posix(error, path);
376 int razor_file_open(const char *path, int flags, mode_t mode,
377 struct razor_error **error)
381 retval = open(path, flags, mode);
384 razor_set_error_posix(error, path);
389 int razor_file_move(const char *path, const char *dest,
390 struct razor_error **error)
395 wchar_t *oldbuf, *newbuf;
396 const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
398 newbuf = razor_utf8_to_utf16(dest, -1);
399 oldbuf = razor_utf8_to_utf16(path, -1);
402 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
403 * cover every case we care about _except_ replacing an empty
404 * directory with a file. Calling RemoveDirectory() will deal
405 * with this case while having no effect in all other cases.
407 (void)RemoveDirectoryW(newbuf);
409 if (!MoveFileExW(oldbuf, newbuf, flags)) {
410 razor_set_error_mswin(error, newbuf, GetLastError());
420 if (rename(path, dest)) {
423 if (access(path, F_OK) < 0)
427 razor_set_error(error, RAZOR_POSIX_ERROR, code, object,
438 static char *absolute_path(const char *path)
441 char *result, *subpath, *p, *s, *t;
443 result = realpath(path, NULL);
445 if (!result && errno == ENOENT) {
451 result = strdup("/");
456 subpath = realpath(p, NULL);
460 len = strlen(subpath);
461 result = malloc(len + strlen(s) + 1);
462 memcpy(result, subpath, len);
463 strcpy(result + len, s);
466 } else if (errno != ENOENT)
475 result = realpath(".", NULL);
484 int razor_file_is_directory(const char *path, struct razor_error **error)
488 if (stat(path, &st) < 0) {
489 razor_set_error_posix(error, path);
493 return !!S_ISDIR(st.st_mode);
496 char *razor_file_mkdtemp_near(const char *path, const char *template,
497 struct razor_error **error)
502 if (path[0]=='\\' && path[1]=='\\' && path[2] && path[2]!='\\'
503 && strchr(path+3,'\\')) {
504 /* We have a UNC path: \\servername\sharename... */
505 const char *sharename, *root;
508 sharename = strchr(path+3,'\\')+1;
509 root = strchr(sharename,'\\');
511 disklen = root - path;
513 disklen = strlen(path);
515 retval = malloc(disklen + 1 + strlen(template) + 1);
516 memcpy(retval, path, disklen);
517 retval[disklen] = '\\';
518 strcpy(retval + disklen + 1, template);
519 } else if ((*path>='A' && *path<='Z' || *path>='a' && *path<='z') &&
521 retval = malloc(3 + strlen(template) + 1);
525 strcpy(retval + 3, template);
531 n = GetCurrentDirectoryW(0, NULL);
532 buf = malloc(n * sizeof(wchar_t));
534 if (GetCurrentDirectoryW(n, buf)) {
535 dir = razor_utf16_to_utf8(buf, n - 1);
537 retval = razor_file_mkdtemp_near(dir, template, error);
541 retval = malloc(3 + strlen(template) + 1);
545 strcpy(retval + 3, template);
552 * Find the mount point (assuming we can write to the
553 * whole filesystem). Otherwise stop at the first
554 * unwritable directory and take one step back.
556 char *s, *abspath, saved;
557 int len, can_step_back = 0;
561 abspath = absolute_path(path);
563 razor_set_error_posix(error, path);
567 if (stat(abspath, &buf) < 0) {
571 razor_set_error_posix(error, abspath);
576 filesystem = buf.st_dev;
578 len = strlen(abspath);
579 while(len > 1 && (s = strrchr(abspath, '/'))) {
583 len = s + 1 - abspath;
589 if (stat(abspath, &buf) < 0) {
593 razor_set_error_posix(error, abspath);
597 } else if (!filesystem)
598 filesystem = buf.st_dev;
600 if (buf.st_dev != filesystem || access(abspath, W_OK)) {
607 len = strlen(abspath);
614 len = 0; /* Avoid an unslightly double slash. */
615 retval = malloc(len + 1 + strlen(template) + 1);
616 memcpy(retval, abspath, len);
618 strcpy(retval + len + 1, template);
623 if (!mkdtemp(retval)) {
628 char *s = strdup(template);
631 if (stat(".", &buf) < 0) {
632 razor_set_error_posix(error, ".");
637 if (buf.st_dev != filesystem)
639 * Don't use a different filesystem. It will
640 * only fail later on (in rename) and cause
641 * an unhelpful error message (EXDEV).
655 razor_set_error(error, RAZOR_POSIX_ERROR, err, retval,
676 #define OPEN_DIR_USED (1U<<0)
678 struct array open_dirs = { 0, };
680 void *razor_file_opendir(const char *path, struct razor_error **error)
682 struct open_dir *od, *odend;
684 odend = open_dirs.data + open_dirs.size;
685 for (od = open_dirs.data; od < odend; od++)
686 if (!(od->flags & OPEN_DIR_USED))
689 od = array_add(&open_dirs, sizeof *od);
694 od->path2 = razor_utf8_to_utf16(path, -1);
695 od->dp = _wopendir(od->path2);
697 od->path = strdup(path);
698 od->dp = opendir(od->path);
703 razor_set_error_mswin(error, od->path2, GetLastError());
706 razor_set_error_posix(error, od->path);
712 od->flags = OPEN_DIR_USED;
716 char *razor_file_readdir(void *dp, struct razor_error **error)
718 struct open_dir *od = dp, *odend;
720 struct _wdirent *dirp;
726 odend = open_dirs.data + open_dirs.size;
727 if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
733 while((dirp = _wreaddir(od->dp))) {
734 path = razor_utf16_to_utf8(dirp->d_name, -1);
735 if (strcmp(path, ".") && strcmp(path, ".."))
741 while((dirp = readdir(od->dp)))
742 if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, ".."))
743 return strdup(dirp->d_name);
748 razor_set_error_mswin(error, od->path2, GetLastError());
750 razor_set_error_posix(error, od->path);
757 int razor_file_closedir(void *dp, struct razor_error **error)
759 struct open_dir *od = dp, *odend;
762 odend = open_dirs.data + open_dirs.size;
763 if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
768 * I can't find documentation to state that _wclosedir()
769 * returns -1 on failure, so be paranoid.
771 retval = _wclosedir(od->dp) ? -1 : 0;
773 retval = closedir(od->dp);
778 razor_set_error_mswin(error, od->path2, GetLastError());
780 razor_set_error_posix(error, od->path);
790 od->flags &= ~OPEN_DIR_USED;
792 for (od = open_dirs.data; od < odend; od++)
793 if (od->flags & OPEN_DIR_USED)
797 array_release(&open_dirs);
798 array_init(&open_dirs);
804 struct razor_uri_vtable_entry {
805 struct razor_uri_vtable vtable;
807 struct array open_files, open_directories;
810 static struct array razor_uri_vtable_entries;
812 static struct razor_uri_vtable_entry *
813 razor_uri_get_vtable_entry(const char *scheme)
815 struct razor_uri_vtable_entry *entry, *eend, *fallback = NULL;
817 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
818 for(entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
819 if (!strcmp0(entry->scheme, scheme))
821 else if (!entry->scheme)
829 razor_uri_mkdir(const char *uri, mode_t mode, struct razor_error **error)
834 struct razor_uri_vtable_entry *entry;
835 struct razor_error *tmp_error = NULL;
837 if (razor_uri_parse(&ru, uri, error))
840 path = razor_path_from_parsed_uri(&ru, &tmp_error);
842 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
843 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
844 razor_error_free(tmp_error);
846 razor_propagate_error(error, tmp_error, NULL);
847 razor_uri_destroy(&ru);
852 razor_uri_destroy(&ru);
853 retval = razor_file_mkdir(path, mode, error);
856 entry = razor_uri_get_vtable_entry(ru.scheme);
857 razor_uri_destroy(&ru);
858 if (!entry || !entry->vtable.mkdir) {
860 razor_set_error(error, RAZOR_GENERAL_ERROR,
861 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
862 uri, "No URI handler installed");
864 retval = entry->vtable.mkdir(uri, mode, error);
871 razor_uri_unlink(const char *uri, struct razor_error **error)
876 struct razor_uri_vtable_entry *entry;
877 struct razor_error *tmp_error = NULL;
879 if (razor_uri_parse(&ru, uri, error))
882 path = razor_path_from_parsed_uri(&ru, &tmp_error);
884 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
885 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
886 razor_error_free(tmp_error);
888 razor_propagate_error(error, tmp_error, NULL);
889 razor_uri_destroy(&ru);
894 razor_uri_destroy(&ru);
895 retval = razor_file_unlink(path, error);
898 entry = razor_uri_get_vtable_entry(ru.scheme);
899 razor_uri_destroy(&ru);
900 if (!entry || !entry->vtable.unlink) {
902 razor_set_error(error, RAZOR_GENERAL_ERROR,
903 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
904 uri, "No URI handler installed");
906 retval = entry->vtable.unlink(uri, error);
913 razor_uri_open(const char *uri, int flags, mode_t mode,
914 struct razor_error **error)
919 struct razor_uri_vtable_entry *entry;
920 struct razor_error *tmp_error = NULL;
922 if (razor_uri_parse(&ru, uri, error))
925 path = razor_path_from_parsed_uri(&ru, &tmp_error);
927 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
928 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
929 razor_error_free(tmp_error);
931 razor_propagate_error(error, tmp_error, NULL);
932 razor_uri_destroy(&ru);
937 razor_uri_destroy(&ru);
938 retval = razor_file_open(path, flags, mode, error);
941 entry = razor_uri_get_vtable_entry(ru.scheme);
942 razor_uri_destroy(&ru);
943 if (!entry || !entry->vtable.open) {
945 razor_set_error(error, RAZOR_GENERAL_ERROR,
946 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
947 uri, "No URI handler installed");
949 retval = entry->vtable.open(uri, flags, mode, error);
956 razor_uri_move(const char *src_uri, const char *dst_uri,
957 struct razor_error **error)
960 char *src_path, *dst_path;
961 struct razor_uri src_ru, dst_ru;
962 struct razor_uri_vtable_entry *entry;
963 struct razor_error *tmp_error = NULL;
965 if (razor_uri_parse(&src_ru, src_uri, error) ||
966 razor_uri_parse(&dst_ru, dst_uri, error))
969 src_path = razor_path_from_parsed_uri(&src_ru, &tmp_error);
971 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
972 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
973 razor_error_free(tmp_error);
974 else if (!src_path) {
975 razor_propagate_error(error, tmp_error, NULL);
976 razor_uri_destroy(&src_ru);
980 dst_path = razor_path_from_parsed_uri(&dst_ru, &tmp_error);
982 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
983 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
984 razor_error_free(tmp_error);
985 else if (!dst_path) {
986 razor_propagate_error(error, tmp_error, NULL);
987 razor_uri_destroy(&dst_ru);
988 razor_uri_destroy(&src_ru);
993 if (src_path && dst_path)
994 retval = razor_file_move(src_path, dst_path, error);
996 if (!strcmp(src_ru.scheme, dst_ru.scheme))
997 entry = razor_uri_get_vtable_entry(src_ru.scheme);
1000 if (entry && entry->vtable.move)
1001 retval = entry->vtable.move(src_uri, dst_uri, error);
1002 else if (strcmp(src_ru.scheme, dst_ru.scheme)) {
1004 razor_set_error(error, RAZOR_GENERAL_ERROR,
1005 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1006 dst_uri, "Cross-scheme URI move");
1009 razor_set_error(error, RAZOR_GENERAL_ERROR,
1010 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1011 src_path ? dst_uri : src_uri,
1012 "No URI handler installed");
1016 razor_uri_destroy(&src_ru);
1017 razor_uri_destroy(&dst_ru);
1025 razor_uri_get_contents(const char *uri, size_t *length, int private,
1026 struct razor_error **error)
1030 struct razor_uri ru;
1031 struct razor_uri_vtable_entry *entry;
1032 struct razor_error *tmp_error = NULL;
1034 if (razor_uri_parse(&ru, uri, error))
1037 path = razor_path_from_parsed_uri(&ru, &tmp_error);
1039 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1040 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1041 razor_error_free(tmp_error);
1043 razor_propagate_error(error, tmp_error, NULL);
1044 razor_uri_destroy(&ru);
1049 razor_uri_destroy(&ru);
1050 retval = razor_file_get_contents(path, length, private, error);
1053 entry = razor_uri_get_vtable_entry(ru.scheme);
1054 razor_uri_destroy(&ru);
1055 if (!entry || !entry->vtable.get_contents) {
1057 razor_set_error(error, RAZOR_GENERAL_ERROR,
1058 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1059 uri, "No URI handler installed");
1061 retval = entry->vtable.get_contents(uri, length,
1064 ptr_array_add(&entry->open_files, retval);
1071 RAZOR_EXPORT int razor_uri_free_contents(void *addr, size_t length)
1074 struct razor_uri_vtable_entry *entry, *eend;
1079 retval = razor_file_free_contents(addr, length);
1084 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1085 for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1086 of = ptr_array_find(&entry->open_files, addr);
1088 if (entry->vtable.free_contents)
1089 retval = entry->vtable.free_contents(addr,
1091 ptr_array_remove_index(&entry->open_files, of);
1100 razor_uri_is_directory(const char *uri, struct razor_error **error)
1104 struct razor_uri ru;
1105 struct razor_uri_vtable_entry *entry;
1106 struct razor_error *tmp_error = NULL;
1108 if (razor_uri_parse(&ru, uri, error))
1111 path = razor_path_from_parsed_uri(&ru, &tmp_error);
1113 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1114 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1115 razor_error_free(tmp_error);
1117 razor_propagate_error(error, tmp_error, NULL);
1118 razor_uri_destroy(&ru);
1123 razor_uri_destroy(&ru);
1124 retval = razor_file_is_directory(path, error);
1127 entry = razor_uri_get_vtable_entry(ru.scheme);
1128 razor_uri_destroy(&ru);
1129 if (!entry || !entry->vtable.is_directory) {
1131 razor_set_error(error, RAZOR_GENERAL_ERROR,
1132 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1133 uri, "No URI handler installed");
1135 retval = entry->vtable.is_directory(uri, error);
1142 razor_uri_mkdtemp_near(const char *uri, const char *template,
1143 struct razor_error **error)
1147 struct razor_uri ru;
1148 struct razor_uri_vtable_entry *entry;
1149 struct razor_error *tmp_error = NULL;
1151 if (razor_uri_parse(&ru, uri, error))
1154 path = razor_path_from_parsed_uri(&ru, &tmp_error);
1156 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1157 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1158 razor_error_free(tmp_error);
1160 razor_propagate_error(error, tmp_error, NULL);
1161 razor_uri_destroy(&ru);
1166 razor_uri_destroy(&ru);
1167 s = razor_file_mkdtemp_near(path, template, error);
1169 retval = razor_path_to_uri(s);
1175 entry = razor_uri_get_vtable_entry(ru.scheme);
1176 razor_uri_destroy(&ru);
1177 if (!entry || !entry->vtable.mkdtemp_near) {
1179 razor_set_error(error, RAZOR_GENERAL_ERROR,
1180 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1181 uri, "No URI handler installed");
1183 retval = entry->vtable.mkdtemp_near(uri, template,
1191 razor_uri_opendir(const char *uri, struct razor_error **error)
1195 struct razor_uri ru;
1196 struct razor_uri_vtable_entry *entry;
1197 struct razor_error *tmp_error = NULL;
1199 if (razor_uri_parse(&ru, uri, error))
1202 path = razor_path_from_parsed_uri(&ru, &tmp_error);
1204 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1205 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1206 razor_error_free(tmp_error);
1208 razor_propagate_error(error, tmp_error, NULL);
1209 razor_uri_destroy(&ru);
1214 razor_uri_destroy(&ru);
1215 retval = razor_file_opendir(path, error);
1218 entry = razor_uri_get_vtable_entry(ru.scheme);
1219 razor_uri_destroy(&ru);
1220 if (!entry || !entry->vtable.opendir) {
1222 razor_set_error(error, RAZOR_GENERAL_ERROR,
1223 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1224 uri, "No URI handler installed");
1226 retval = entry->vtable.opendir(uri, error);
1228 ptr_array_add(&entry->open_directories, retval);
1235 RAZOR_EXPORT char *razor_uri_readdir(void *dir, struct razor_error **error)
1239 struct razor_uri_vtable_entry *entry, *eend;
1241 retval = razor_file_readdir(dir, error);
1243 if (retval != (char *)-1)
1246 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1247 for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1248 od = ptr_array_find(&entry->open_directories, dir);
1250 if (entry->vtable.readdir)
1251 retval = entry->vtable.readdir(dir, error);
1256 if (retval == (char *)-1) {
1258 razor_set_error(error, RAZOR_GENERAL_ERROR,
1259 RAZOR_GENERAL_ERROR_FAILED, NULL,
1260 "Invalid directory handle");
1266 RAZOR_EXPORT int razor_uri_closedir(void *dir, struct razor_error **error)
1270 struct razor_uri_vtable_entry *entry, *eend;
1272 retval = razor_file_closedir(dir, error);
1277 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1278 for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1279 od = ptr_array_find(&entry->open_directories, dir);
1281 if (entry->vtable.closedir)
1282 retval = entry->vtable.closedir(dir, error);
1288 razor_set_error(error, RAZOR_GENERAL_ERROR,
1289 RAZOR_GENERAL_ERROR_FAILED, NULL,
1290 "Invalid directory handle");
1295 RAZOR_EXPORT int razor_uri_set_vtable(const char *scheme,
1296 struct razor_uri_vtable *vtable, struct razor_error **error)
1298 struct razor_uri_vtable_entry *entry, *eend;
1300 if (!strcmp0(scheme, "file")) {
1302 * There's no fundamental reason why we couldn't support
1303 * this, but it's a lot of work without any obvious need.
1305 razor_set_error(error, RAZOR_GENERAL_ERROR,
1306 RAZOR_GENERAL_ERROR_FAILED, scheme,
1307 "Can't override file scheme handler");
1311 if (vtable->structure_size < sizeof(struct razor_uri_vtable)) {
1313 * A future version of the API might add vfuncs to the
1314 * table (which we ignore), but won't remove any.
1316 razor_set_error(error, RAZOR_GENERAL_ERROR,
1317 RAZOR_GENERAL_ERROR_FAILED, scheme,
1318 "Invalid vtable structure size");
1322 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1323 for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1324 if (!strcmp0(entry->scheme, scheme))
1328 if (entry == eend) {
1331 entry = array_add(&razor_uri_vtable_entries, sizeof *entry);
1332 entry->scheme = scheme ? strdup(scheme) : NULL;
1333 array_init(&entry->open_files);
1334 array_init(&entry->open_directories);
1335 } else if (entry->open_files.size || entry->open_directories.size) {
1336 razor_set_error(error, RAZOR_GENERAL_ERROR,
1337 RAZOR_GENERAL_ERROR_FAILED, scheme,
1338 "URI handler busy");
1343 entry->vtable = *vtable;
1344 entry->vtable.structure_size = sizeof(struct razor_uri_vtable);
1346 free(entry->scheme);
1347 if (entry + 1 < eend)
1348 memmove(entry, entry + 1, eend - (entry + 1));
1349 array_set_size(&razor_uri_vtable_entries,
1350 razor_uri_vtable_entries.size - sizeof *entry);