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)
60 razor_archive_get_file_contents(const char *filename, int fd, const char *path,
61 size_t *length, struct razor_error **error)
69 struct archive_entry *entry;
71 a = archive_read_new();
72 archive_read_support_compression_all(a);
73 archive_read_support_format_all(a);
75 r = archive_read_open_fd(a, fd, 10240);
78 razor_set_error(error, RAZOR_POSIX_ERROR, archive_errno(a),
79 filename, archive_error_string(a));
80 archive_read_finish(a);
85 r = archive_read_next_header(a, &entry);
88 else if (r < ARCHIVE_WARN) {
89 razor_set_error(error, RAZOR_POSIX_ERROR,
90 archive_errno(a), filename,
91 archive_error_string(a));
92 archive_read_close(a);
93 archive_read_finish(a);
98 * TODO: Unicode support. Might need to wait for libarchive v4.
100 if (!strcmp(archive_entry_pathname(entry), path)) {
101 addr = malloc(archive_entry_size(entry));
103 archive_read_close(a);
104 archive_read_finish(a);
105 razor_set_error(error, RAZOR_POSIX_ERROR,
107 "Not enough memory");
112 r = archive_read_data_block(a, &buf, &size,
114 if (r == ARCHIVE_EOF)
116 if (r < ARCHIVE_OK) {
117 razor_set_error(error, RAZOR_POSIX_ERROR,
118 archive_errno(a), path,
119 archive_error_string(a));
124 memcpy((char *)addr + offset, buf, size);
127 archive_read_close(a);
128 archive_read_finish(a);
134 archive_read_close(a);
135 archive_read_finish(a);
137 razor_set_error(error, RAZOR_POSIX_ERROR, ENOENT, path,
138 "No such file or directory in archive");
144 razor_file_get_contents_archive(const char *filename, size_t *length,
145 struct razor_error **error)
148 char *path, *slash, *s;
151 path = strdup(filename);
152 slash = strrchr(path, '/');
156 fd = open(path, O_RDONLY | O_BINARY);
158 addr = razor_archive_get_file_contents(path, fd,
165 } else if (errno != ENOTDIR && errno != ENOENT) {
167 } else if (errno != ENOTDIR) {
170 razor_set_error_posix(error, filename);
173 s = strrchr(path, '/');
180 razor_set_error(error, RAZOR_POSIX_ERROR, ENOENT, filename,
183 razor_set_error(error, RAZOR_POSIX_ERROR, ENOTDIR, filename,
190 #define OPEN_FILE_USED (1U<<0)
191 #define OPEN_FILE_MMAPPED (1U<<1)
199 struct array open_files = { 0, };
201 void *razor_file_get_contents(const char *filename, size_t *length, int private,
202 struct razor_error **error)
209 struct open_file *of, *ofend;
211 fd = open(filename, O_RDONLY | O_BINARY);
215 if (errno != ENOTDIR && errno != ENOENT) {
217 if (errno != ENOTDIR) {
219 razor_set_error_posix(error, filename);
222 addr = razor_file_get_contents_archive(filename, &size, error);
226 razor_set_error_posix(error, filename);
230 if (fstat(fd, &st) < 0) {
231 razor_set_error_posix(error, filename);
238 ofend = open_files.data + open_files.size;
239 for (of = open_files.data; of < ofend; of++)
240 if (!(of->flags & OPEN_FILE_USED))
243 of = array_add(&open_files, sizeof *of);
248 if (!addr && !private) {
249 addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
250 if (addr == MAP_FAILED)
253 of->flags = OPEN_FILE_USED | OPEN_FILE_MMAPPED;
255 #endif /* HAVE_SYS_MMAN_H */
262 res = read(fd, addr + nb, size - nb);
264 razor_set_error_posix(error, filename);
272 of->flags = OPEN_FILE_USED;
274 razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
275 "Not enough memory");
290 int razor_file_free_contents(void *addr, size_t length)
295 struct open_file *of, *ofend;
297 ofend = open_files.data + open_files.size;
298 for (of = open_files.data; of < ofend; of++)
299 if ((of->flags & OPEN_FILE_USED) && of->addr == addr)
307 mmapped = of->flags & OPEN_FILE_MMAPPED;
309 of->flags &= ~OPEN_FILE_USED;
311 for (of = open_files.data; of < ofend; of++)
312 if (of->flags & OPEN_FILE_USED)
316 array_release(&open_files);
317 array_init(&open_files);
322 return munmap(addr, length);
329 int razor_file_mkdir(const char *path, mode_t mode, struct razor_error **error)
334 retval = mkdir(path, mode);
338 * Ignore the case of a pre-existing directory and give
339 * an explicit error message if there is something other
340 * than a directory already at path.
343 if (code != EEXIST || stat(path, &buf))
344 razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
346 else if (!S_ISDIR(buf.st_mode))
347 razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
354 int razor_file_unlink(const char *path, struct razor_error **error)
358 retval = unlink(path);
361 razor_set_error_posix(error, path);
366 int razor_file_open(const char *path, int flags, mode_t mode,
367 struct razor_error **error)
371 retval = open(path, flags, mode);
374 razor_set_error_posix(error, path);
379 int razor_file_move(const char *path, const char *dest,
380 struct razor_error **error)
385 wchar_t *oldbuf, *newbuf;
386 const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
388 newbuf = razor_utf8_to_utf16(dest, -1);
389 oldbuf = razor_utf8_to_utf16(path, -1);
392 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
393 * cover every case we care about _except_ replacing an empty
394 * directory with a file. Calling RemoveDirectory() will deal
395 * with this case while having no effect in all other cases.
397 (void)RemoveDirectoryW(newbuf);
399 if (!MoveFileExW(oldbuf, newbuf, flags)) {
400 razor_set_error_mswin(error, newbuf, GetLastError());
410 if (rename(path, dest)) {
413 if (access(path, F_OK) < 0)
417 razor_set_error(error, RAZOR_POSIX_ERROR, code, object,
428 static char *absolute_path(const char *path)
431 char *result, *subpath, *p, *s, *t;
433 result = realpath(path, NULL);
435 if (!result && errno == ENOENT) {
441 result = strdup("/");
446 subpath = realpath(p, NULL);
450 len = strlen(subpath);
451 result = malloc(len + strlen(s) + 1);
452 memcpy(result, subpath, len);
453 strcpy(result + len, s);
456 } else if (errno != ENOENT)
465 result = realpath(".", NULL);
474 int razor_file_is_directory(const char *path, struct razor_error **error)
478 if (stat(path, &st) < 0) {
479 razor_set_error_posix(error, path);
483 return !!S_ISDIR(st.st_mode);
486 char *razor_file_mkdtemp_near(const char *path, const char *template,
487 struct razor_error **error)
492 if (path[0]=='\\' && path[1]=='\\' && path[2] && path[2]!='\\'
493 && strchr(path+3,'\\')) {
494 /* We have a UNC path: \\servername\sharename... */
495 const char *sharename, *root;
498 sharename = strchr(path+3,'\\')+1;
499 root = strchr(sharename,'\\');
501 disklen = root - path;
503 disklen = strlen(path);
505 retval = malloc(disklen + 1 + strlen(template) + 1);
506 memcpy(retval, path, disklen);
507 retval[disklen] = '\\';
508 strcpy(retval + disklen + 1, template);
509 } else if ((*path>='A' && *path<='Z' || *path>='a' && *path<='z') &&
511 retval = malloc(3 + strlen(template) + 1);
515 strcpy(retval + 3, template);
521 n = GetCurrentDirectoryW(0, NULL);
522 buf = malloc(n * sizeof(wchar_t));
524 if (GetCurrentDirectoryW(n, buf)) {
525 dir = razor_utf16_to_utf8(buf, n - 1);
527 retval = razor_file_mkdtemp_near(dir, template, error);
531 retval = malloc(3 + strlen(template) + 1);
535 strcpy(retval + 3, template);
542 * Find the mount point (assuming we can write to the
543 * whole filesystem). Otherwise stop at the first
544 * unwritable directory and take one step back.
546 char *s, *abspath, saved;
547 int len, can_step_back = 0;
551 abspath = absolute_path(path);
553 razor_set_error_posix(error, path);
557 if (stat(abspath, &buf) < 0) {
561 razor_set_error_posix(error, abspath);
566 filesystem = buf.st_dev;
568 len = strlen(abspath);
569 while(len > 1 && (s = strrchr(abspath, '/'))) {
573 len = s + 1 - abspath;
579 if (stat(abspath, &buf) < 0) {
583 razor_set_error_posix(error, abspath);
587 } else if (!filesystem)
588 filesystem = buf.st_dev;
590 if (buf.st_dev != filesystem || access(abspath, W_OK)) {
597 len = strlen(abspath);
604 len = 0; /* Avoid an unslightly double slash. */
605 retval = malloc(len + 1 + strlen(template) + 1);
606 memcpy(retval, abspath, len);
608 strcpy(retval + len + 1, template);
613 if (!mkdtemp(retval)) {
618 char *s = strdup(template);
621 if (stat(".", &buf) < 0) {
622 razor_set_error_posix(error, ".");
627 if (buf.st_dev != filesystem)
629 * Don't use a different filesystem. It will
630 * only fail later on (in rename) and cause
631 * an unhelpful error message (EXDEV).
645 razor_set_error(error, RAZOR_POSIX_ERROR, err, retval,
666 #define OPEN_DIR_USED (1U<<0)
668 struct array open_dirs = { 0, };
670 void *razor_file_opendir(const char *path, struct razor_error **error)
672 struct open_dir *od, *odend;
674 odend = open_dirs.data + open_dirs.size;
675 for (od = open_dirs.data; od < odend; od++)
676 if (!(od->flags & OPEN_DIR_USED))
679 od = array_add(&open_dirs, sizeof *od);
684 od->path2 = razor_utf8_to_utf16(path, -1);
685 od->dp = _wopendir(od->path2);
687 od->path = strdup(path);
688 od->dp = opendir(od->path);
693 razor_set_error_mswin(error, od->path2, GetLastError());
696 razor_set_error_posix(error, od->path);
702 od->flags = OPEN_DIR_USED;
706 char *razor_file_readdir(void *dp, struct razor_error **error)
708 struct open_dir *od = dp, *odend;
710 struct _wdirent *dirp;
716 odend = open_dirs.data + open_dirs.size;
717 if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
723 while((dirp = _wreaddir(od->dp))) {
724 path = razor_utf16_to_utf8(dirp->d_name, -1);
725 if (strcmp(path, ".") && strcmp(path, ".."))
731 while((dirp = readdir(od->dp)))
732 if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, ".."))
733 return strdup(dirp->d_name);
738 razor_set_error_mswin(error, od->path2, GetLastError());
740 razor_set_error_posix(error, od->path);
747 int razor_file_closedir(void *dp, struct razor_error **error)
749 struct open_dir *od = dp, *odend;
752 odend = open_dirs.data + open_dirs.size;
753 if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
758 * I can't find documentation to state that _wclosedir()
759 * returns -1 on failure, so be paranoid.
761 retval = _wclosedir(od->dp) ? -1 : 0;
763 retval = closedir(od->dp);
768 razor_set_error_mswin(error, od->path2, GetLastError());
770 razor_set_error_posix(error, od->path);
780 od->flags &= ~OPEN_DIR_USED;
782 for (od = open_dirs.data; od < odend; od++)
783 if (od->flags & OPEN_DIR_USED)
787 array_release(&open_dirs);
788 array_init(&open_dirs);
794 struct razor_uri_vtable_entry {
795 struct razor_uri_vtable vtable;
797 struct array open_files, open_directories;
800 static struct array razor_uri_vtable_entries;
802 static struct razor_uri_vtable_entry *
803 razor_uri_get_vtable_entry(const char *scheme)
805 struct razor_uri_vtable_entry *entry, *eend, *fallback = NULL;
807 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
808 for(entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
809 if (!strcmp0(entry->scheme, scheme))
811 else if (!entry->scheme)
819 razor_uri_mkdir(const char *uri, mode_t mode, struct razor_error **error)
824 struct razor_uri_vtable_entry *entry;
825 struct razor_error *tmp_error = NULL;
827 if (razor_uri_parse(&ru, uri, error))
830 path = razor_path_from_parsed_uri(&ru, &tmp_error);
832 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
833 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
834 razor_error_free(tmp_error);
836 razor_propagate_error(error, tmp_error, NULL);
837 razor_uri_destroy(&ru);
842 razor_uri_destroy(&ru);
843 retval = razor_file_mkdir(path, mode, error);
846 entry = razor_uri_get_vtable_entry(ru.scheme);
847 razor_uri_destroy(&ru);
848 if (!entry || !entry->vtable.mkdir) {
850 razor_set_error(error, RAZOR_GENERAL_ERROR,
851 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
852 uri, "No URI handler installed");
854 retval = entry->vtable.mkdir(uri, mode, error);
861 razor_uri_unlink(const char *uri, struct razor_error **error)
866 struct razor_uri_vtable_entry *entry;
867 struct razor_error *tmp_error = NULL;
869 if (razor_uri_parse(&ru, uri, error))
872 path = razor_path_from_parsed_uri(&ru, &tmp_error);
874 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
875 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
876 razor_error_free(tmp_error);
878 razor_propagate_error(error, tmp_error, NULL);
879 razor_uri_destroy(&ru);
884 razor_uri_destroy(&ru);
885 retval = razor_file_unlink(path, error);
888 entry = razor_uri_get_vtable_entry(ru.scheme);
889 razor_uri_destroy(&ru);
890 if (!entry || !entry->vtable.unlink) {
892 razor_set_error(error, RAZOR_GENERAL_ERROR,
893 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
894 uri, "No URI handler installed");
896 retval = entry->vtable.unlink(uri, error);
903 razor_uri_open(const char *uri, int flags, mode_t mode,
904 struct razor_error **error)
909 struct razor_uri_vtable_entry *entry;
910 struct razor_error *tmp_error = NULL;
912 if (razor_uri_parse(&ru, uri, error))
915 path = razor_path_from_parsed_uri(&ru, &tmp_error);
917 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
918 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
919 razor_error_free(tmp_error);
921 razor_propagate_error(error, tmp_error, NULL);
922 razor_uri_destroy(&ru);
927 razor_uri_destroy(&ru);
928 retval = razor_file_open(path, flags, mode, error);
931 entry = razor_uri_get_vtable_entry(ru.scheme);
932 razor_uri_destroy(&ru);
933 if (!entry || !entry->vtable.open) {
935 razor_set_error(error, RAZOR_GENERAL_ERROR,
936 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
937 uri, "No URI handler installed");
939 retval = entry->vtable.open(uri, flags, mode, error);
946 razor_uri_move(const char *src_uri, const char *dst_uri,
947 struct razor_error **error)
950 char *src_path, *dst_path;
951 struct razor_uri src_ru, dst_ru;
952 struct razor_uri_vtable_entry *entry;
953 struct razor_error *tmp_error = NULL;
955 if (razor_uri_parse(&src_ru, src_uri, error) ||
956 razor_uri_parse(&dst_ru, dst_uri, error))
959 src_path = razor_path_from_parsed_uri(&src_ru, &tmp_error);
961 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
962 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
963 razor_error_free(tmp_error);
964 else if (!src_path) {
965 razor_propagate_error(error, tmp_error, NULL);
966 razor_uri_destroy(&src_ru);
970 dst_path = razor_path_from_parsed_uri(&dst_ru, &tmp_error);
972 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
973 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
974 razor_error_free(tmp_error);
975 else if (!dst_path) {
976 razor_propagate_error(error, tmp_error, NULL);
977 razor_uri_destroy(&dst_ru);
978 razor_uri_destroy(&src_ru);
983 if (src_path && dst_path)
984 retval = razor_file_move(src_path, dst_path, error);
986 if (!strcmp(src_ru.scheme, dst_ru.scheme))
987 entry = razor_uri_get_vtable_entry(src_ru.scheme);
990 if (entry && entry->vtable.move)
991 retval = entry->vtable.move(src_uri, dst_uri, error);
992 else if (strcmp(src_ru.scheme, dst_ru.scheme)) {
994 razor_set_error(error, RAZOR_GENERAL_ERROR,
995 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
996 dst_uri, "Cross-scheme URI move");
999 razor_set_error(error, RAZOR_GENERAL_ERROR,
1000 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1001 src_path ? dst_uri : src_uri,
1002 "No URI handler installed");
1006 razor_uri_destroy(&src_ru);
1007 razor_uri_destroy(&dst_ru);
1015 razor_uri_get_contents(const char *uri, size_t *length, int private,
1016 struct razor_error **error)
1020 struct razor_uri ru;
1021 struct razor_uri_vtable_entry *entry;
1022 struct razor_error *tmp_error = NULL;
1024 if (razor_uri_parse(&ru, uri, error))
1027 path = razor_path_from_parsed_uri(&ru, &tmp_error);
1029 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1030 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1031 razor_error_free(tmp_error);
1033 razor_propagate_error(error, tmp_error, NULL);
1034 razor_uri_destroy(&ru);
1039 razor_uri_destroy(&ru);
1040 retval = razor_file_get_contents(path, length, private, error);
1043 entry = razor_uri_get_vtable_entry(ru.scheme);
1044 razor_uri_destroy(&ru);
1045 if (!entry || !entry->vtable.get_contents) {
1047 razor_set_error(error, RAZOR_GENERAL_ERROR,
1048 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1049 uri, "No URI handler installed");
1051 retval = entry->vtable.get_contents(uri, length,
1054 ptr_array_add(&entry->open_files, retval);
1061 RAZOR_EXPORT int razor_uri_free_contents(void *addr, size_t length)
1064 struct razor_uri_vtable_entry *entry, *eend;
1069 retval = razor_file_free_contents(addr, length);
1074 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1075 for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1076 of = ptr_array_find(&entry->open_files, addr);
1078 if (entry->vtable.free_contents)
1079 retval = entry->vtable.free_contents(addr,
1081 ptr_array_remove_index(&entry->open_files, of);
1090 razor_uri_is_directory(const char *uri, struct razor_error **error)
1094 struct razor_uri ru;
1095 struct razor_uri_vtable_entry *entry;
1096 struct razor_error *tmp_error = NULL;
1098 if (razor_uri_parse(&ru, uri, error))
1101 path = razor_path_from_parsed_uri(&ru, &tmp_error);
1103 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1104 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1105 razor_error_free(tmp_error);
1107 razor_propagate_error(error, tmp_error, NULL);
1108 razor_uri_destroy(&ru);
1113 razor_uri_destroy(&ru);
1114 retval = razor_file_is_directory(path, error);
1117 entry = razor_uri_get_vtable_entry(ru.scheme);
1118 razor_uri_destroy(&ru);
1119 if (!entry || !entry->vtable.is_directory) {
1121 razor_set_error(error, RAZOR_GENERAL_ERROR,
1122 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1123 uri, "No URI handler installed");
1125 retval = entry->vtable.is_directory(uri, error);
1132 razor_uri_mkdtemp_near(const char *uri, const char *template,
1133 struct razor_error **error)
1137 struct razor_uri ru;
1138 struct razor_uri_vtable_entry *entry;
1139 struct razor_error *tmp_error = NULL;
1141 if (razor_uri_parse(&ru, uri, error))
1144 path = razor_path_from_parsed_uri(&ru, &tmp_error);
1146 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1147 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1148 razor_error_free(tmp_error);
1150 razor_propagate_error(error, tmp_error, NULL);
1151 razor_uri_destroy(&ru);
1156 razor_uri_destroy(&ru);
1157 s = razor_file_mkdtemp_near(path, template, error);
1159 retval = razor_path_to_uri(s);
1165 entry = razor_uri_get_vtable_entry(ru.scheme);
1166 razor_uri_destroy(&ru);
1167 if (!entry || !entry->vtable.mkdtemp_near) {
1169 razor_set_error(error, RAZOR_GENERAL_ERROR,
1170 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1171 uri, "No URI handler installed");
1173 retval = entry->vtable.mkdtemp_near(uri, template,
1181 razor_uri_opendir(const char *uri, struct razor_error **error)
1185 struct razor_uri ru;
1186 struct razor_uri_vtable_entry *entry;
1187 struct razor_error *tmp_error = NULL;
1189 if (razor_uri_parse(&ru, uri, error))
1192 path = razor_path_from_parsed_uri(&ru, &tmp_error);
1194 if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
1195 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
1196 razor_error_free(tmp_error);
1198 razor_propagate_error(error, tmp_error, NULL);
1199 razor_uri_destroy(&ru);
1204 razor_uri_destroy(&ru);
1205 retval = razor_file_opendir(path, error);
1208 entry = razor_uri_get_vtable_entry(ru.scheme);
1209 razor_uri_destroy(&ru);
1210 if (!entry || !entry->vtable.opendir) {
1212 razor_set_error(error, RAZOR_GENERAL_ERROR,
1213 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
1214 uri, "No URI handler installed");
1216 retval = entry->vtable.opendir(uri, error);
1218 ptr_array_add(&entry->open_directories, retval);
1225 RAZOR_EXPORT char *razor_uri_readdir(void *dir, struct razor_error **error)
1229 struct razor_uri_vtable_entry *entry, *eend;
1231 retval = razor_file_readdir(dir, error);
1233 if (retval != (char *)-1)
1236 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1237 for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1238 od = ptr_array_find(&entry->open_directories, dir);
1240 if (entry->vtable.readdir)
1241 retval = entry->vtable.readdir(dir, error);
1246 if (retval == (char *)-1) {
1248 razor_set_error(error, RAZOR_GENERAL_ERROR,
1249 RAZOR_GENERAL_ERROR_FAILED, NULL,
1250 "Invalid directory handle");
1256 RAZOR_EXPORT int razor_uri_closedir(void *dir, struct razor_error **error)
1260 struct razor_uri_vtable_entry *entry, *eend;
1262 retval = razor_file_closedir(dir, error);
1267 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1268 for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1269 od = ptr_array_find(&entry->open_directories, dir);
1271 if (entry->vtable.closedir)
1272 retval = entry->vtable.closedir(dir, error);
1278 razor_set_error(error, RAZOR_GENERAL_ERROR,
1279 RAZOR_GENERAL_ERROR_FAILED, NULL,
1280 "Invalid directory handle");
1285 RAZOR_EXPORT int razor_uri_set_vtable(const char *scheme,
1286 struct razor_uri_vtable *vtable, struct razor_error **error)
1288 struct razor_uri_vtable_entry *entry, *eend;
1290 if (!strcmp0(scheme, "file")) {
1292 * There's no fundamental reason why we couldn't support
1293 * this, but it's a lot of work without any obvious need.
1295 razor_set_error(error, RAZOR_GENERAL_ERROR,
1296 RAZOR_GENERAL_ERROR_FAILED, scheme,
1297 "Can't override file scheme handler");
1301 if (vtable->structure_size < sizeof(struct razor_uri_vtable)) {
1303 * A future version of the API might add vfuncs to the
1304 * table (which we ignore), but won't remove any.
1306 razor_set_error(error, RAZOR_GENERAL_ERROR,
1307 RAZOR_GENERAL_ERROR_FAILED, scheme,
1308 "Invalid vtable structure size");
1312 eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
1313 for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
1314 if (!strcmp0(entry->scheme, scheme))
1318 if (entry == eend) {
1321 entry = array_add(&razor_uri_vtable_entries, sizeof *entry);
1322 entry->scheme = scheme ? strdup(scheme) : NULL;
1323 array_init(&entry->open_files);
1324 array_init(&entry->open_directories);
1325 } else if (entry->open_files.size || entry->open_directories.size) {
1326 razor_set_error(error, RAZOR_GENERAL_ERROR,
1327 RAZOR_GENERAL_ERROR_FAILED, scheme,
1328 "URI handler busy");
1333 entry->vtable = *vtable;
1334 entry->vtable.structure_size = sizeof(struct razor_uri_vtable);
1336 free(entry->scheme);
1337 if (entry + 1 < eend)
1338 memmove(entry, entry + 1, eend - (entry + 1));
1339 array_set_size(&razor_uri_vtable_entries,
1340 razor_uri_vtable_entries.size - sizeof *entry);