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, const char *path,
60 size_t *length, struct razor_error **error)
67 size_t size, total_size;
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_filename(a, filename, 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 total_size = archive_entry_size(entry);
109 addr = malloc(total_size);
111 archive_read_close(a);
112 archive_read_finish(a);
113 razor_set_error(error, RAZOR_POSIX_ERROR,
115 "Not enough memory");
120 r = archive_read_data_block(a, &buf, &size,
122 if (r == ARCHIVE_EOF)
124 if (r < ARCHIVE_OK) {
125 razor_set_error(error, RAZOR_POSIX_ERROR,
126 archive_errno(a), path,
127 archive_error_string(a));
132 memcpy((char *)addr + offset, buf, size);
135 archive_read_close(a);
136 archive_read_finish(a);
138 *length = total_size;
144 archive_read_close(a);
145 archive_read_finish(a);
147 razor_set_error(error, RAZOR_POSIX_ERROR, ENOENT, path,
148 "No such file or directory in archive");
152 razor_set_error(error, RAZOR_GENERAL_ERROR,
153 RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE,
154 filename, "Archives are not supported in this build");
157 #endif /* HAVE_LIBARCHIVE */
161 razor_file_get_contents_archive(const char *filename, size_t *length,
162 struct razor_error **error)
165 char *path, *slash, *s;
168 path = strdup(filename);
169 slash = strrchr(path, '/');
173 is_dir = razor_file_is_directory(path, NULL);
175 s = strrchr(path, '/');
178 } else if (!is_dir) {
179 addr = razor_archive_get_file_contents(path, slash + 1,
188 razor_set_error(error, RAZOR_POSIX_ERROR, ENOTDIR, filename,
193 #define OPEN_FILE_USED (1U<<0)
194 #define OPEN_FILE_MMAPPED (1U<<1)
202 struct array open_files = { 0, };
204 void *razor_file_get_contents(const char *filename, size_t *length, int private,
205 struct razor_error **error)
212 struct open_file *of, *ofend;
214 fd = open(filename, O_RDONLY | O_BINARY);
218 if (saved_errno != ENOTDIR && saved_errno != ENOENT) {
220 if (saved_errno != ENOTDIR) {
222 razor_set_error_posix(error, filename);
225 addr = razor_file_get_contents_archive(filename, &size, error);
227 if (error && saved_errno != ENOTDIR &&
228 razor_error_matches(*error, RAZOR_POSIX_ERROR,
230 razor_error_free(*error);
232 razor_set_error(error, RAZOR_POSIX_ERROR,
233 saved_errno, filename,
234 strerror(saved_errno));
239 if (fstat(fd, &st) < 0) {
240 razor_set_error_posix(error, filename);
247 ofend = open_files.data + open_files.size;
248 for (of = open_files.data; of < ofend; of++)
249 if (!(of->flags & OPEN_FILE_USED))
252 of = array_add(&open_files, sizeof *of);
257 if (!addr && !private) {
258 addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
259 if (addr == MAP_FAILED)
262 of->flags = OPEN_FILE_USED | OPEN_FILE_MMAPPED;
264 #endif /* HAVE_SYS_MMAN_H */
271 res = read(fd, addr + nb, size - nb);
273 razor_set_error_posix(error, filename);
281 of->flags = OPEN_FILE_USED;
283 razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
284 "Not enough memory");
299 int razor_file_free_contents(void *addr, size_t length)
304 struct open_file *of, *ofend;
306 ofend = open_files.data + open_files.size;
307 for (of = open_files.data; of < ofend; of++)
308 if ((of->flags & OPEN_FILE_USED) && of->addr == addr)
316 mmapped = of->flags & OPEN_FILE_MMAPPED;
318 of->flags &= ~OPEN_FILE_USED;
320 for (of = open_files.data; of < ofend; of++)
321 if (of->flags & OPEN_FILE_USED)
325 array_release(&open_files);
326 array_init(&open_files);
331 return munmap(addr, length);
338 int razor_file_mkdir(const char *path, mode_t mode, struct razor_error **error)
343 retval = mkdir(path, mode);
347 * Ignore the case of a pre-existing directory and give
348 * an explicit error message if there is something other
349 * than a directory already at path.
352 if (code != EEXIST || stat(path, &buf))
353 razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
355 else if (!S_ISDIR(buf.st_mode))
356 razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
363 int razor_file_unlink(const char *path, struct razor_error **error)
367 retval = unlink(path);
370 razor_set_error_posix(error, path);
375 int razor_file_open(const char *path, int flags, mode_t mode,
376 struct razor_error **error)
380 retval = open(path, flags, mode);
383 razor_set_error_posix(error, path);
388 int razor_file_move(const char *path, const char *dest,
389 struct razor_error **error)
394 wchar_t *oldbuf, *newbuf;
395 const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
397 newbuf = razor_utf8_to_utf16(dest, -1);
398 oldbuf = razor_utf8_to_utf16(path, -1);
401 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
402 * cover every case we care about _except_ replacing an empty
403 * directory with a file. Calling RemoveDirectory() will deal
404 * with this case while having no effect in all other cases.
406 (void)RemoveDirectoryW(newbuf);
408 if (!MoveFileExW(oldbuf, newbuf, flags)) {
409 razor_set_error_mswin(error, newbuf, GetLastError());
419 if (rename(path, dest)) {
422 if (access(path, F_OK) < 0)
426 razor_set_error(error, RAZOR_POSIX_ERROR, code, object,
437 static char *absolute_path(const char *path)
440 char *result, *subpath, *p, *s, *t;
442 result = realpath(path, NULL);
444 if (!result && errno == ENOENT) {
450 result = strdup("/");
455 subpath = realpath(p, NULL);
459 len = strlen(subpath);
460 result = malloc(len + strlen(s) + 1);
461 memcpy(result, subpath, len);
462 strcpy(result + len, s);
465 } else if (errno != ENOENT)
474 result = realpath(".", NULL);
483 int razor_file_is_directory(const char *path, struct razor_error **error)
487 if (stat(path, &st) < 0) {
488 razor_set_error_posix(error, path);
492 return !!S_ISDIR(st.st_mode);
495 char *razor_file_mkdtemp_near(const char *path, const char *template,
496 struct razor_error **error)
501 if (path[0]=='\\' && path[1]=='\\' && path[2] && path[2]!='\\'
502 && strchr(path+3,'\\')) {
503 /* We have a UNC path: \\servername\sharename... */
504 const char *sharename, *root;
507 sharename = strchr(path+3,'\\')+1;
508 root = strchr(sharename,'\\');
510 disklen = root - path;
512 disklen = strlen(path);
514 retval = malloc(disklen + 1 + strlen(template) + 1);
515 memcpy(retval, path, disklen);
516 retval[disklen] = '\\';
517 strcpy(retval + disklen + 1, template);
518 } else if ((*path>='A' && *path<='Z' || *path>='a' && *path<='z') &&
520 retval = malloc(3 + strlen(template) + 1);
524 strcpy(retval + 3, template);
530 n = GetCurrentDirectoryW(0, NULL);
531 buf = malloc(n * sizeof(wchar_t));
533 if (GetCurrentDirectoryW(n, buf)) {
534 dir = razor_utf16_to_utf8(buf, n - 1);
536 retval = razor_file_mkdtemp_near(dir, template, error);
540 retval = malloc(3 + strlen(template) + 1);
544 strcpy(retval + 3, template);
551 * Find the mount point (assuming we can write to the
552 * whole filesystem). Otherwise stop at the first
553 * unwritable directory and take one step back.
555 char *s, *abspath, saved;
556 int len, can_step_back = 0;
560 abspath = absolute_path(path);
562 razor_set_error_posix(error, path);
566 if (stat(abspath, &buf) < 0) {
570 razor_set_error_posix(error, abspath);
575 filesystem = buf.st_dev;
577 len = strlen(abspath);
578 while(len > 1 && (s = strrchr(abspath, '/'))) {
582 len = s + 1 - abspath;
588 if (stat(abspath, &buf) < 0) {
592 razor_set_error_posix(error, abspath);
596 } else if (!filesystem)
597 filesystem = buf.st_dev;
599 if (buf.st_dev != filesystem || access(abspath, W_OK)) {
606 len = strlen(abspath);
613 len = 0; /* Avoid an unslightly double slash. */
614 retval = malloc(len + 1 + strlen(template) + 1);
615 memcpy(retval, abspath, len);
617 strcpy(retval + len + 1, template);
622 if (!mkdtemp(retval)) {
627 char *s = strdup(template);
630 if (stat(".", &buf) < 0) {
631 razor_set_error_posix(error, ".");
636 if (buf.st_dev != filesystem)
638 * Don't use a different filesystem. It will
639 * only fail later on (in rename) and cause
640 * an unhelpful error message (EXDEV).
654 razor_set_error(error, RAZOR_POSIX_ERROR, err, retval,
675 #define OPEN_DIR_USED (1U<<0)
677 struct array open_dirs = { 0, };
679 void *razor_file_opendir(const char *path, struct razor_error **error)
681 struct open_dir *od, *odend;
683 odend = open_dirs.data + open_dirs.size;
684 for (od = open_dirs.data; od < odend; od++)
685 if (!(od->flags & OPEN_DIR_USED))
688 od = array_add(&open_dirs, sizeof *od);
693 od->path2 = razor_utf8_to_utf16(path, -1);
694 od->dp = _wopendir(od->path2);
696 od->path = strdup(path);
697 od->dp = opendir(od->path);
702 razor_set_error_mswin(error, od->path2, GetLastError());
705 razor_set_error_posix(error, od->path);
711 od->flags = OPEN_DIR_USED;
715 char *razor_file_readdir(void *dp, struct razor_error **error)
717 struct open_dir *od = dp, *odend;
719 struct _wdirent *dirp;
725 odend = open_dirs.data + open_dirs.size;
726 if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
732 while((dirp = _wreaddir(od->dp))) {
733 path = razor_utf16_to_utf8(dirp->d_name, -1);
734 if (strcmp(path, ".") && strcmp(path, ".."))
740 while((dirp = readdir(od->dp)))
741 if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, ".."))
742 return strdup(dirp->d_name);
747 razor_set_error_mswin(error, od->path2, GetLastError());
749 razor_set_error_posix(error, od->path);
756 int razor_file_closedir(void *dp, struct razor_error **error)
758 struct open_dir *od = dp, *odend;
761 odend = open_dirs.data + open_dirs.size;
762 if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
767 * I can't find documentation to state that _wclosedir()
768 * returns -1 on failure, so be paranoid.
770 retval = _wclosedir(od->dp) ? -1 : 0;
772 retval = closedir(od->dp);
777 razor_set_error_mswin(error, od->path2, GetLastError());
779 razor_set_error_posix(error, od->path);
789 od->flags &= ~OPEN_DIR_USED;
791 for (od = open_dirs.data; od < odend; od++)
792 if (od->flags & OPEN_DIR_USED)
796 array_release(&open_dirs);
797 array_init(&open_dirs);
803 struct razor_uri_vtable_entry {
804 struct razor_uri_vtable vtable;
806 struct array open_files, open_directories;
809 static struct array razor_uri_vtable_entries;
811 static struct razor_uri_vtable_entry *
812 razor_uri_get_vtable_entry(const char *scheme)
814 struct razor_uri_vtable_entry *entry, *eend, *fallback = NULL;
816 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
817 for(entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
818 if (!strcmp0(entry->scheme, scheme))
820 else if (!entry->scheme)
828 razor_uri_mkdir(const char *uri, mode_t mode, struct razor_error **error)
833 struct razor_uri_vtable_entry *entry;
834 struct razor_error *tmp_error = NULL;
836 if (razor_uri_parse(&ru, uri, error))
839 path = razor_path_from_parsed_uri(&ru, &tmp_error);
841 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
842 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
843 razor_error_free(tmp_error);
845 razor_propagate_error(error, tmp_error, NULL);
846 razor_uri_destroy(&ru);
851 razor_uri_destroy(&ru);
852 retval = razor_file_mkdir(path, mode, error);
855 entry = razor_uri_get_vtable_entry(ru.scheme);
856 razor_uri_destroy(&ru);
857 if (!entry || !entry->vtable.mkdir) {
859 razor_set_error(error, RAZOR_GENERAL_ERROR,
860 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
861 uri, "No URI handler installed");
863 retval = entry->vtable.mkdir(uri, mode, error);
870 razor_uri_unlink(const char *uri, struct razor_error **error)
875 struct razor_uri_vtable_entry *entry;
876 struct razor_error *tmp_error = NULL;
878 if (razor_uri_parse(&ru, uri, error))
881 path = razor_path_from_parsed_uri(&ru, &tmp_error);
883 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
884 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
885 razor_error_free(tmp_error);
887 razor_propagate_error(error, tmp_error, NULL);
888 razor_uri_destroy(&ru);
893 razor_uri_destroy(&ru);
894 retval = razor_file_unlink(path, error);
897 entry = razor_uri_get_vtable_entry(ru.scheme);
898 razor_uri_destroy(&ru);
899 if (!entry || !entry->vtable.unlink) {
901 razor_set_error(error, RAZOR_GENERAL_ERROR,
902 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
903 uri, "No URI handler installed");
905 retval = entry->vtable.unlink(uri, error);
912 razor_uri_open(const char *uri, int flags, mode_t mode,
913 struct razor_error **error)
918 struct razor_uri_vtable_entry *entry;
919 struct razor_error *tmp_error = NULL;
921 if (razor_uri_parse(&ru, uri, error))
924 path = razor_path_from_parsed_uri(&ru, &tmp_error);
926 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
927 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
928 razor_error_free(tmp_error);
930 razor_propagate_error(error, tmp_error, NULL);
931 razor_uri_destroy(&ru);
936 razor_uri_destroy(&ru);
937 retval = razor_file_open(path, flags, mode, error);
940 entry = razor_uri_get_vtable_entry(ru.scheme);
941 razor_uri_destroy(&ru);
942 if (!entry || !entry->vtable.open) {
944 razor_set_error(error, RAZOR_GENERAL_ERROR,
945 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
946 uri, "No URI handler installed");
948 retval = entry->vtable.open(uri, flags, mode, error);
955 razor_uri_move(const char *src_uri, const char *dst_uri,
956 struct razor_error **error)
959 char *src_path, *dst_path;
960 struct razor_uri src_ru, dst_ru;
961 struct razor_uri_vtable_entry *entry;
962 struct razor_error *tmp_error = NULL;
964 if (razor_uri_parse(&src_ru, src_uri, error) ||
965 razor_uri_parse(&dst_ru, dst_uri, error))
968 src_path = razor_path_from_parsed_uri(&src_ru, &tmp_error);
970 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
971 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
972 razor_error_free(tmp_error);
973 else if (!src_path) {
974 razor_propagate_error(error, tmp_error, NULL);
975 razor_uri_destroy(&src_ru);
979 dst_path = razor_path_from_parsed_uri(&dst_ru, &tmp_error);
981 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
982 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
983 razor_error_free(tmp_error);
984 else if (!dst_path) {
985 razor_propagate_error(error, tmp_error, NULL);
986 razor_uri_destroy(&dst_ru);
987 razor_uri_destroy(&src_ru);
992 if (src_path && dst_path)
993 retval = razor_file_move(src_path, dst_path, error);
995 if (!strcmp(src_ru.scheme, dst_ru.scheme))
996 entry = razor_uri_get_vtable_entry(src_ru.scheme);
999 if (entry && entry->vtable.move)
1000 retval = entry->vtable.move(src_uri, dst_uri, error);
1001 else if (strcmp(src_ru.scheme, dst_ru.scheme)) {
1003 razor_set_error(error, RAZOR_GENERAL_ERROR,
1004 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1005 dst_uri, "Cross-scheme URI move");
1008 razor_set_error(error, RAZOR_GENERAL_ERROR,
1009 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1010 src_path ? dst_uri : src_uri,
1011 "No URI handler installed");
1015 razor_uri_destroy(&src_ru);
1016 razor_uri_destroy(&dst_ru);
1024 razor_uri_get_contents(const char *uri, size_t *length, int private,
1025 struct razor_error **error)
1029 struct razor_uri ru;
1030 struct razor_uri_vtable_entry *entry;
1031 struct razor_error *tmp_error = NULL;
1033 if (razor_uri_parse(&ru, uri, error))
1036 path = razor_path_from_parsed_uri(&ru, &tmp_error);
1038 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1039 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1040 razor_error_free(tmp_error);
1042 razor_propagate_error(error, tmp_error, NULL);
1043 razor_uri_destroy(&ru);
1048 razor_uri_destroy(&ru);
1049 retval = razor_file_get_contents(path, length, private, error);
1052 entry = razor_uri_get_vtable_entry(ru.scheme);
1053 razor_uri_destroy(&ru);
1054 if (!entry || !entry->vtable.get_contents) {
1056 razor_set_error(error, RAZOR_GENERAL_ERROR,
1057 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1058 uri, "No URI handler installed");
1060 retval = entry->vtable.get_contents(uri, length,
1063 ptr_array_add(&entry->open_files, retval);
1070 RAZOR_EXPORT int razor_uri_free_contents(void *addr, size_t length)
1073 struct razor_uri_vtable_entry *entry, *eend;
1078 retval = razor_file_free_contents(addr, length);
1083 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1084 for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1085 of = ptr_array_find(&entry->open_files, addr);
1087 if (entry->vtable.free_contents)
1088 retval = entry->vtable.free_contents(addr,
1090 ptr_array_remove_index(&entry->open_files, of);
1099 razor_uri_is_directory(const char *uri, struct razor_error **error)
1103 struct razor_uri ru;
1104 struct razor_uri_vtable_entry *entry;
1105 struct razor_error *tmp_error = NULL;
1107 if (razor_uri_parse(&ru, uri, error))
1110 path = razor_path_from_parsed_uri(&ru, &tmp_error);
1112 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1113 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1114 razor_error_free(tmp_error);
1116 razor_propagate_error(error, tmp_error, NULL);
1117 razor_uri_destroy(&ru);
1122 razor_uri_destroy(&ru);
1123 retval = razor_file_is_directory(path, error);
1126 entry = razor_uri_get_vtable_entry(ru.scheme);
1127 razor_uri_destroy(&ru);
1128 if (!entry || !entry->vtable.is_directory) {
1130 razor_set_error(error, RAZOR_GENERAL_ERROR,
1131 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1132 uri, "No URI handler installed");
1134 retval = entry->vtable.is_directory(uri, error);
1141 razor_uri_mkdtemp_near(const char *uri, const char *template,
1142 struct razor_error **error)
1146 struct razor_uri ru;
1147 struct razor_uri_vtable_entry *entry;
1148 struct razor_error *tmp_error = NULL;
1150 if (razor_uri_parse(&ru, uri, error))
1153 path = razor_path_from_parsed_uri(&ru, &tmp_error);
1155 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1156 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1157 razor_error_free(tmp_error);
1159 razor_propagate_error(error, tmp_error, NULL);
1160 razor_uri_destroy(&ru);
1165 razor_uri_destroy(&ru);
1166 s = razor_file_mkdtemp_near(path, template, error);
1168 retval = razor_path_to_uri(s);
1174 entry = razor_uri_get_vtable_entry(ru.scheme);
1175 razor_uri_destroy(&ru);
1176 if (!entry || !entry->vtable.mkdtemp_near) {
1178 razor_set_error(error, RAZOR_GENERAL_ERROR,
1179 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1180 uri, "No URI handler installed");
1182 retval = entry->vtable.mkdtemp_near(uri, template,
1190 razor_uri_opendir(const char *uri, struct razor_error **error)
1194 struct razor_uri ru;
1195 struct razor_uri_vtable_entry *entry;
1196 struct razor_error *tmp_error = NULL;
1198 if (razor_uri_parse(&ru, uri, error))
1201 path = razor_path_from_parsed_uri(&ru, &tmp_error);
1203 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1204 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1205 razor_error_free(tmp_error);
1207 razor_propagate_error(error, tmp_error, NULL);
1208 razor_uri_destroy(&ru);
1213 razor_uri_destroy(&ru);
1214 retval = razor_file_opendir(path, error);
1217 entry = razor_uri_get_vtable_entry(ru.scheme);
1218 razor_uri_destroy(&ru);
1219 if (!entry || !entry->vtable.opendir) {
1221 razor_set_error(error, RAZOR_GENERAL_ERROR,
1222 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1223 uri, "No URI handler installed");
1225 retval = entry->vtable.opendir(uri, error);
1227 ptr_array_add(&entry->open_directories, retval);
1234 RAZOR_EXPORT char *razor_uri_readdir(void *dir, struct razor_error **error)
1238 struct razor_uri_vtable_entry *entry, *eend;
1240 retval = razor_file_readdir(dir, error);
1242 if (retval != (char *)-1)
1245 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1246 for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1247 od = ptr_array_find(&entry->open_directories, dir);
1249 if (entry->vtable.readdir)
1250 retval = entry->vtable.readdir(dir, error);
1255 if (retval == (char *)-1) {
1257 razor_set_error(error, RAZOR_GENERAL_ERROR,
1258 RAZOR_GENERAL_ERROR_FAILED, NULL,
1259 "Invalid directory handle");
1265 RAZOR_EXPORT int razor_uri_closedir(void *dir, struct razor_error **error)
1269 struct razor_uri_vtable_entry *entry, *eend;
1271 retval = razor_file_closedir(dir, error);
1276 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1277 for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1278 od = ptr_array_find(&entry->open_directories, dir);
1280 if (entry->vtable.closedir)
1281 retval = entry->vtable.closedir(dir, error);
1287 razor_set_error(error, RAZOR_GENERAL_ERROR,
1288 RAZOR_GENERAL_ERROR_FAILED, NULL,
1289 "Invalid directory handle");
1294 RAZOR_EXPORT int razor_uri_set_vtable(const char *scheme,
1295 struct razor_uri_vtable *vtable, struct razor_error **error)
1297 struct razor_uri_vtable_entry *entry, *eend;
1299 if (!strcmp0(scheme, "file")) {
1301 * There's no fundamental reason why we couldn't support
1302 * this, but it's a lot of work without any obvious need.
1304 razor_set_error(error, RAZOR_GENERAL_ERROR,
1305 RAZOR_GENERAL_ERROR_FAILED, scheme,
1306 "Can't override file scheme handler");
1310 if (vtable->structure_size < sizeof(struct razor_uri_vtable)) {
1312 * A future version of the API might add vfuncs to the
1313 * table (which we ignore), but won't remove any.
1315 razor_set_error(error, RAZOR_GENERAL_ERROR,
1316 RAZOR_GENERAL_ERROR_FAILED, scheme,
1317 "Invalid vtable structure size");
1321 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1322 for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1323 if (!strcmp0(entry->scheme, scheme))
1327 if (entry == eend) {
1330 entry = array_add(&razor_uri_vtable_entries, sizeof *entry);
1331 entry->scheme = scheme ? strdup(scheme) : NULL;
1332 array_init(&entry->open_files);
1333 array_init(&entry->open_directories);
1334 } else if (entry->open_files.size || entry->open_directories.size) {
1335 razor_set_error(error, RAZOR_GENERAL_ERROR,
1336 RAZOR_GENERAL_ERROR_FAILED, scheme,
1337 "URI handler busy");
1342 entry->vtable = *vtable;
1343 entry->vtable.structure_size = sizeof(struct razor_uri_vtable);
1345 free(entry->scheme);
1346 if (entry + 1 < eend)
1347 memmove(entry, entry + 1, eend - (entry + 1));
1348 array_set_size(&razor_uri_vtable_entries,
1349 razor_uri_vtable_entries.size - sizeof *entry);