librazor/uri-io.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Jun 08 18:02:49 2018 +0100 (2018-06-08)
changeset 501 850be6a6885c
parent 487 a5837882a252
permissions -rw-r--r--
Added tag 0.7 for changeset f98d77376544
ali@475
     1
/*
ali@475
     2
 * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
ali@475
     3
 * Copyright (C) 2008  Red Hat, Inc
ali@475
     4
 * Copyright (C) 2009, 2011, 2012, 2014, 2016  J. Ali Harlow <ali@juiblex.co.uk>
ali@475
     5
 *
ali@475
     6
 * This program is free software; you can redistribute it and/or modify
ali@475
     7
 * it under the terms of the GNU General Public License as published by
ali@475
     8
 * the Free Software Foundation; either version 2 of the License, or
ali@475
     9
 * (at your option) any later version.
ali@475
    10
 *
ali@475
    11
 * This program is distributed in the hope that it will be useful,
ali@475
    12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
ali@475
    13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
ali@475
    14
 * GNU General Public License for more details.
ali@475
    15
 *
ali@475
    16
 * You should have received a copy of the GNU General Public License along
ali@475
    17
 * with this program; if not, write to the Free Software Foundation, Inc.,
ali@475
    18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
ali@475
    19
 */
ali@475
    20
ali@475
    21
#include "config.h"
ali@475
    22
ali@475
    23
#include <limits.h>
ali@475
    24
#include <string.h>
ali@475
    25
#include <sys/types.h>
ali@475
    26
#include <sys/stat.h>
ali@475
    27
#include <stdlib.h>
ali@475
    28
#include <stdio.h>
ali@475
    29
#include <stdint.h>
ali@475
    30
#include <errno.h>
ali@475
    31
#include <unistd.h>
ali@475
    32
#include <fcntl.h>
ali@475
    33
#include <dirent.h>
ali@475
    34
#ifdef MSWIN_API
ali@475
    35
#include <windows.h>
ali@475
    36
#include <direct.h>
ali@475
    37
#endif
ali@475
    38
#if HAVE_SYS_MMAN_H
ali@475
    39
#include <sys/mman.h>
ali@475
    40
#endif
ali@476
    41
#if HAVE_LIBARCHIVE
ali@476
    42
#include <archive.h>
ali@476
    43
#include <archive_entry.h>
ali@476
    44
#endif
ali@475
    45
#include <assert.h>
ali@475
    46
ali@475
    47
#include "razor.h"
ali@475
    48
#include "types/types.h"
ali@475
    49
#include "razor-internal.h"
ali@475
    50
ali@475
    51
#ifndef O_BINARY
ali@475
    52
#define O_BINARY	0
ali@475
    53
#endif
ali@475
    54
ali@475
    55
#define strcmp0(s1, s2)		((s1) && (s2) ? strcmp(s1, s2) : \
ali@475
    56
				 (s1) ? 1 : (s2) ? -1 : 0)
ali@475
    57
ali@476
    58
static void *
ali@487
    59
razor_archive_get_file_contents(const char *filename, const char *path,
ali@476
    60
				size_t *length, struct razor_error **error)
ali@476
    61
{
ali@483
    62
#if HAVE_LIBARCHIVE
ali@476
    63
	int r;
ali@483
    64
	const char *errmsg;
ali@476
    65
	void *addr;
ali@476
    66
	const void *buf;
ali@485
    67
	size_t size, total_size;
ali@476
    68
	off_t offset;
ali@476
    69
	struct archive *a;
ali@476
    70
	struct archive_entry *entry;
ali@476
    71
ali@476
    72
	a = archive_read_new();
ali@476
    73
	archive_read_support_compression_all(a);
ali@476
    74
	archive_read_support_format_all(a);
ali@476
    75
ali@487
    76
	r = archive_read_open_filename(a, filename, 10240);
ali@476
    77
ali@476
    78
	if (r) {
ali@483
    79
		errmsg = archive_error_string(a);
ali@483
    80
		if (!strcmp(errmsg, "Unrecognized archive format"))
ali@483
    81
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@483
    82
					RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE,
ali@483
    83
					filename, errmsg);
ali@483
    84
		else
ali@483
    85
			razor_set_error(error, RAZOR_POSIX_ERROR,
ali@483
    86
					archive_errno(a), filename, errmsg);
ali@476
    87
		archive_read_finish(a);
ali@476
    88
		return NULL;
ali@476
    89
	}
ali@476
    90
ali@476
    91
	for (;;) {
ali@476
    92
		r = archive_read_next_header(a, &entry);
ali@476
    93
		if (r == ARCHIVE_EOF)
ali@476
    94
			break;
ali@476
    95
		else if (r < ARCHIVE_WARN) {
ali@476
    96
			razor_set_error(error, RAZOR_POSIX_ERROR,
ali@476
    97
					archive_errno(a), filename,
ali@476
    98
					archive_error_string(a));
ali@476
    99
			archive_read_close(a);
ali@476
   100
			archive_read_finish(a);
ali@476
   101
			return NULL;
ali@476
   102
		}
ali@476
   103
ali@476
   104
		/*
ali@476
   105
		 * TODO: Unicode support. Might need to wait for libarchive v4.
ali@476
   106
		 */
ali@476
   107
		if (!strcmp(archive_entry_pathname(entry), path)) {
ali@485
   108
			total_size = archive_entry_size(entry);
ali@485
   109
			addr = malloc(total_size);
ali@476
   110
			if (!addr) {
ali@476
   111
				archive_read_close(a);
ali@476
   112
				archive_read_finish(a);
ali@476
   113
				razor_set_error(error, RAZOR_POSIX_ERROR,
ali@476
   114
						ENOMEM, NULL,
ali@476
   115
						"Not enough memory");
ali@476
   116
				return NULL;
ali@476
   117
			}
ali@476
   118
ali@476
   119
			for(;;) {
ali@476
   120
				r = archive_read_data_block(a, &buf, &size,
ali@476
   121
							    &offset);
ali@476
   122
				if (r == ARCHIVE_EOF)
ali@476
   123
					break;
ali@476
   124
				if (r < ARCHIVE_OK) {
ali@476
   125
					razor_set_error(error, RAZOR_POSIX_ERROR,
ali@476
   126
							archive_errno(a), path,
ali@476
   127
							archive_error_string(a));
ali@476
   128
					free(addr);
ali@476
   129
					addr = NULL;
ali@476
   130
					break;
ali@476
   131
				}
ali@476
   132
				memcpy((char *)addr + offset, buf, size);
ali@476
   133
			}
ali@476
   134
ali@476
   135
			archive_read_close(a);
ali@476
   136
			archive_read_finish(a);
ali@476
   137
ali@485
   138
			*length = total_size;
ali@485
   139
ali@476
   140
			return addr;
ali@476
   141
		}
ali@476
   142
	}
ali@476
   143
ali@476
   144
	archive_read_close(a);
ali@476
   145
	archive_read_finish(a);
ali@476
   146
ali@476
   147
	razor_set_error(error, RAZOR_POSIX_ERROR, ENOENT, path,
ali@476
   148
			"No such file or directory in archive");
ali@476
   149
ali@476
   150
	return NULL;
ali@483
   151
#else
ali@483
   152
	razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@483
   153
			RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE,
ali@483
   154
			filename, "Archives are not supported in this build");
ali@483
   155
ali@483
   156
	return NULL;
ali@483
   157
#endif	/* HAVE_LIBARCHIVE */
ali@476
   158
}
ali@476
   159
ali@476
   160
static void *
ali@476
   161
razor_file_get_contents_archive(const char *filename, size_t *length,
ali@476
   162
				struct razor_error **error)
ali@476
   163
{
ali@487
   164
	int is_dir;
ali@476
   165
	char *path, *slash, *s;
ali@476
   166
	void *addr;
ali@476
   167
ali@476
   168
	path = strdup(filename);
ali@476
   169
	slash = strrchr(path, '/');
ali@476
   170
ali@476
   171
	while (slash) {
ali@476
   172
		*slash = '\0';
ali@487
   173
		is_dir = razor_file_is_directory(path, NULL);
ali@487
   174
		if (is_dir < 0) {
ali@487
   175
			s = strrchr(path, '/');
ali@487
   176
			*slash = '/';
ali@487
   177
			slash = s;
ali@487
   178
		} else if (!is_dir) {
ali@487
   179
			addr = razor_archive_get_file_contents(path, slash + 1,
ali@487
   180
							       length, error);
ali@476
   181
			free(path);
ali@476
   182
			return addr;
ali@487
   183
		} else
ali@487
   184
			break;
ali@476
   185
	}
ali@476
   186
ali@476
   187
	free(path);
ali@476
   188
	razor_set_error(error, RAZOR_POSIX_ERROR, ENOTDIR, filename,
ali@476
   189
			strerror(ENOTDIR));
ali@476
   190
	return NULL;
ali@476
   191
}
ali@476
   192
ali@475
   193
#define OPEN_FILE_USED		(1U<<0)
ali@475
   194
#define OPEN_FILE_MMAPPED	(1U<<1)
ali@475
   195
ali@475
   196
struct open_file {
ali@475
   197
	void *addr;
ali@475
   198
	size_t length;
ali@475
   199
	uint32_t flags;
ali@475
   200
};
ali@475
   201
ali@475
   202
struct array open_files = { 0, };
ali@475
   203
ali@475
   204
void *razor_file_get_contents(const char *filename, size_t *length, int private,
ali@475
   205
			      struct razor_error **error)
ali@475
   206
{
ali@487
   207
	int fd, saved_errno;
ali@475
   208
	struct stat st;
ali@475
   209
	void *addr = NULL;
ali@476
   210
	size_t nb, size;
ali@475
   211
	ssize_t res;
ali@475
   212
	struct open_file *of, *ofend;
ali@475
   213
ali@475
   214
	fd = open(filename, O_RDONLY | O_BINARY);
ali@475
   215
	if (fd < 0) {
ali@487
   216
		saved_errno = errno;
ali@481
   217
#ifdef MSWIN_API
ali@487
   218
		if (saved_errno != ENOTDIR && saved_errno != ENOENT) {
ali@481
   219
#else
ali@487
   220
		if (saved_errno != ENOTDIR) {
ali@481
   221
#endif
ali@476
   222
			razor_set_error_posix(error, filename);
ali@476
   223
			return NULL;
ali@476
   224
		}
ali@476
   225
		addr = razor_file_get_contents_archive(filename, &size, error);
ali@487
   226
		if (!addr) {
ali@487
   227
			if (error && saved_errno != ENOTDIR &&
ali@489
   228
			    razor_error_matches(*error, RAZOR_POSIX_ERROR,
ali@487
   229
						ENOTDIR)) {
ali@487
   230
				razor_error_free(*error);
ali@487
   231
				*error = NULL;
ali@487
   232
				razor_set_error(error, RAZOR_POSIX_ERROR,
ali@487
   233
						saved_errno, filename,
ali@487
   234
						strerror(saved_errno));
ali@487
   235
			}
ali@476
   236
			return NULL;
ali@487
   237
		}
ali@476
   238
	} else {
ali@476
   239
		if (fstat(fd, &st) < 0) {
ali@476
   240
			razor_set_error_posix(error, filename);
ali@476
   241
			close(fd);
ali@476
   242
			return NULL;
ali@476
   243
		}
ali@476
   244
		size = st.st_size;
ali@475
   245
	}
ali@475
   246
ali@475
   247
	ofend = open_files.data + open_files.size;
ali@475
   248
	for (of = open_files.data; of < ofend; of++)
ali@475
   249
		if (!(of->flags & OPEN_FILE_USED))
ali@475
   250
			break;
ali@475
   251
	if (of == ofend) {
ali@475
   252
		of = array_add(&open_files, sizeof *of);
ali@475
   253
		of->flags = 0;
ali@475
   254
	}
ali@475
   255
ali@475
   256
#if HAVE_SYS_MMAN_H
ali@476
   257
	if (!addr && !private) {
ali@476
   258
		addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
ali@475
   259
		if (addr == MAP_FAILED)
ali@475
   260
			addr = NULL;
ali@475
   261
		else
ali@475
   262
			of->flags = OPEN_FILE_USED | OPEN_FILE_MMAPPED;
ali@475
   263
	}
ali@475
   264
#endif	/* HAVE_SYS_MMAN_H */
ali@476
   265
ali@475
   266
	if (!addr) {
ali@476
   267
		addr = malloc(size);
ali@475
   268
		if (addr) {
ali@475
   269
			nb = 0;
ali@476
   270
			while(nb < size) {
ali@476
   271
				res = read(fd, addr + nb, size - nb);
ali@475
   272
				if (res <= 0) {
ali@475
   273
					razor_set_error_posix(error, filename);
ali@475
   274
					free(addr);
ali@475
   275
					addr = NULL;
ali@475
   276
					break;
ali@475
   277
				}
ali@475
   278
				nb += res;
ali@475
   279
			}
ali@476
   280
			if (addr)
ali@476
   281
				of->flags = OPEN_FILE_USED;
ali@475
   282
		} else
ali@475
   283
			razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
ali@475
   284
					"Not enough memory");
ali@475
   285
	}
ali@476
   286
ali@476
   287
	if (fd >= 0)
ali@476
   288
		close(fd);
ali@475
   289
ali@475
   290
	of->addr = addr;
ali@476
   291
	of->length = size;
ali@476
   292
ali@476
   293
	if (addr)
ali@476
   294
		*length = size;
ali@475
   295
ali@475
   296
	return addr;
ali@475
   297
}
ali@475
   298
ali@475
   299
int razor_file_free_contents(void *addr, size_t length)
ali@475
   300
{
ali@475
   301
#if HAVE_SYS_MMAN_H
ali@475
   302
	int mmapped;
ali@475
   303
#endif
ali@475
   304
	struct open_file *of, *ofend;
ali@475
   305
ali@475
   306
	ofend = open_files.data + open_files.size;
ali@475
   307
	for (of = open_files.data; of < ofend; of++)
ali@475
   308
		if ((of->flags & OPEN_FILE_USED) && of->addr == addr)
ali@475
   309
			break;
ali@475
   310
ali@475
   311
	if (of == ofend)
ali@475
   312
		return -2;
ali@475
   313
ali@475
   314
	length = of->length;
ali@475
   315
#if HAVE_SYS_MMAN_H
ali@475
   316
	mmapped = of->flags & OPEN_FILE_MMAPPED;
ali@475
   317
#endif
ali@475
   318
	of->flags &= ~OPEN_FILE_USED;
ali@475
   319
ali@475
   320
	for (of = open_files.data; of < ofend; of++)
ali@475
   321
		if (of->flags & OPEN_FILE_USED)
ali@475
   322
			break;
ali@475
   323
ali@475
   324
	if (of == ofend) {
ali@475
   325
		array_release(&open_files);
ali@475
   326
		array_init(&open_files);
ali@475
   327
	}
ali@475
   328
ali@475
   329
#if HAVE_SYS_MMAN_H
ali@475
   330
	if (mmapped)
ali@475
   331
		return munmap(addr, length);
ali@475
   332
#endif
ali@475
   333
ali@475
   334
	free(addr);
ali@475
   335
	return 0;
ali@475
   336
}
ali@475
   337
ali@475
   338
int razor_file_mkdir(const char *path, mode_t mode, struct razor_error **error)
ali@475
   339
{
ali@475
   340
	int retval, code;
ali@475
   341
	struct stat buf;
ali@475
   342
ali@475
   343
	retval = mkdir(path, mode);
ali@475
   344
ali@475
   345
	if (retval) {
ali@475
   346
		/*
ali@475
   347
		 * Ignore the case of a pre-existing directory and give
ali@475
   348
		 * an explicit error message if there is something other
ali@475
   349
		 * than a directory already at path.
ali@475
   350
		 */
ali@475
   351
		code = errno;
ali@475
   352
		if (code != EEXIST || stat(path, &buf))
ali@475
   353
			razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
ali@475
   354
					strerror(code));
ali@475
   355
		else if (!S_ISDIR(buf.st_mode))
ali@475
   356
			razor_set_error(error, RAZOR_POSIX_ERROR, code, path,
ali@475
   357
					"Not a directory");
ali@475
   358
	}
ali@475
   359
ali@475
   360
	return retval;
ali@475
   361
}
ali@475
   362
ali@475
   363
int razor_file_unlink(const char *path, struct razor_error **error)
ali@475
   364
{
ali@475
   365
	int retval;
ali@475
   366
ali@475
   367
	retval = unlink(path);
ali@475
   368
ali@475
   369
	if (retval)
ali@475
   370
		razor_set_error_posix(error, path);
ali@475
   371
ali@475
   372
	return retval;
ali@475
   373
}
ali@475
   374
ali@475
   375
int razor_file_open(const char *path, int flags, mode_t mode,
ali@475
   376
		    struct razor_error **error)
ali@475
   377
{
ali@475
   378
	int retval;
ali@475
   379
ali@475
   380
	retval = open(path, flags, mode);
ali@475
   381
ali@475
   382
	if (retval < 0)
ali@475
   383
		razor_set_error_posix(error, path);
ali@475
   384
ali@475
   385
	return retval;
ali@475
   386
}
ali@475
   387
ali@475
   388
int razor_file_move(const char *path, const char *dest,
ali@475
   389
		    struct razor_error **error)
ali@475
   390
{
ali@475
   391
	int retval = 0;
ali@475
   392
ali@475
   393
#ifdef MSWIN_API
ali@475
   394
	wchar_t *oldbuf, *newbuf;
ali@475
   395
	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
ali@475
   396
ali@475
   397
	newbuf = razor_utf8_to_utf16(dest, -1);
ali@475
   398
	oldbuf = razor_utf8_to_utf16(path, -1);
ali@475
   399
ali@475
   400
	/*
ali@475
   401
	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
ali@475
   402
	 * cover every case we care about _except_ replacing an empty
ali@475
   403
	 * directory with a file. Calling RemoveDirectory() will deal
ali@475
   404
	 * with this case while having no effect in all other cases.
ali@475
   405
	 */
ali@475
   406
	(void)RemoveDirectoryW(newbuf);
ali@475
   407
ali@475
   408
	if (!MoveFileExW(oldbuf, newbuf, flags)) {
ali@475
   409
		razor_set_error_mswin(error, newbuf, GetLastError());
ali@475
   410
		retval = -1;
ali@475
   411
	}
ali@475
   412
ali@475
   413
	free(newbuf);
ali@475
   414
	free(oldbuf);
ali@475
   415
#else
ali@475
   416
	int code;
ali@475
   417
	const char *object;
ali@475
   418
ali@475
   419
	if (rename(path, dest)) {
ali@475
   420
		if (error) {
ali@475
   421
			code = errno;
ali@475
   422
			if (access(path, F_OK) < 0)
ali@475
   423
				object = path;
ali@475
   424
			else
ali@475
   425
				object = dest;
ali@475
   426
			razor_set_error(error, RAZOR_POSIX_ERROR, code, object, 
ali@475
   427
					strerror(code));
ali@475
   428
		}
ali@475
   429
		retval = -1;
ali@475
   430
	}
ali@475
   431
#endif
ali@475
   432
ali@475
   433
	return retval;
ali@475
   434
}
ali@475
   435
ali@475
   436
#ifndef MSWIN_API
ali@475
   437
static char *absolute_path(const char *path)
ali@475
   438
{
ali@475
   439
	int len;
ali@475
   440
	char *result, *subpath, *p, *s, *t;
ali@475
   441
ali@475
   442
	result = realpath(path, NULL);
ali@475
   443
ali@475
   444
	if (!result && errno == ENOENT) {
ali@475
   445
		p = strdup(path);
ali@475
   446
		s = strrchr(p, '/');
ali@475
   447
ali@475
   448
		while (s) {
ali@475
   449
			if (s == p) {
ali@475
   450
				result = strdup("/");
ali@475
   451
				break;
ali@475
   452
			}
ali@475
   453
ali@475
   454
			*s = '\0';
ali@475
   455
			subpath = realpath(p, NULL);
ali@475
   456
ali@475
   457
			if (subpath) {
ali@475
   458
				*s = '/';
ali@475
   459
				len = strlen(subpath);
ali@475
   460
				result = malloc(len + strlen(s) + 1);
ali@475
   461
				memcpy(result, subpath, len);
ali@475
   462
				strcpy(result + len, s);
ali@475
   463
				free(subpath);
ali@475
   464
				break;
ali@475
   465
			} else if (errno != ENOENT)
ali@475
   466
				break;
ali@475
   467
ali@475
   468
			t = strrchr(p, '/');
ali@475
   469
			*s = '/';
ali@475
   470
			s = t;
ali@475
   471
		}
ali@475
   472
ali@475
   473
		if (!s)
ali@475
   474
			result = realpath(".", NULL);
ali@475
   475
ali@475
   476
		free(p);
ali@475
   477
	}
ali@475
   478
ali@475
   479
	return result;
ali@475
   480
}
ali@475
   481
#endif
ali@475
   482
ali@475
   483
int razor_file_is_directory(const char *path, struct razor_error **error)
ali@475
   484
{
ali@475
   485
	struct stat st;
ali@475
   486
ali@475
   487
	if (stat(path, &st) < 0) {
ali@475
   488
		razor_set_error_posix(error, path);
ali@475
   489
		return -1;
ali@475
   490
	}
ali@475
   491
ali@475
   492
	return !!S_ISDIR(st.st_mode);
ali@475
   493
}
ali@475
   494
ali@475
   495
char *razor_file_mkdtemp_near(const char *path, const char *template,
ali@475
   496
			      struct razor_error **error)
ali@475
   497
{
ali@475
   498
	char *retval;
ali@475
   499
ali@475
   500
#ifdef MSWIN_API
ali@475
   501
	if (path[0]=='\\' && path[1]=='\\' && path[2] && path[2]!='\\'
ali@475
   502
	    && strchr(path+3,'\\')) {
ali@475
   503
		/* We have a UNC path: \\servername\sharename... */
ali@475
   504
		const char *sharename, *root;
ali@475
   505
		int disklen;
ali@475
   506
ali@475
   507
		sharename = strchr(path+3,'\\')+1;
ali@475
   508
		root = strchr(sharename,'\\');
ali@475
   509
		if (root)
ali@475
   510
		    disklen = root - path;
ali@475
   511
		else
ali@475
   512
		    disklen = strlen(path);
ali@475
   513
ali@475
   514
		retval = malloc(disklen + 1 + strlen(template) + 1);
ali@475
   515
		memcpy(retval, path, disklen);
ali@475
   516
		retval[disklen] = '\\';
ali@475
   517
		strcpy(retval + disklen + 1, template);
ali@475
   518
	} else if ((*path>='A' && *path<='Z' || *path>='a' && *path<='z') &&
ali@475
   519
		    path[1]==':') {
ali@475
   520
		retval = malloc(3 + strlen(template) + 1);
ali@475
   521
		retval[0] = path[0];
ali@475
   522
		retval[1] = ':';
ali@475
   523
		retval[2] = '\\';
ali@475
   524
		strcpy(retval + 3, template);
ali@475
   525
	} else {
ali@475
   526
		DWORD n;
ali@475
   527
		wchar_t *buf;
ali@475
   528
		char *dir;
ali@475
   529
ali@475
   530
		n = GetCurrentDirectoryW(0, NULL);
ali@475
   531
		buf = malloc(n * sizeof(wchar_t));
ali@475
   532
ali@475
   533
		if (GetCurrentDirectoryW(n, buf)) {
ali@475
   534
			dir = razor_utf16_to_utf8(buf, n - 1);
ali@475
   535
			free(buf);
ali@475
   536
			retval = razor_file_mkdtemp_near(dir, template, error);
ali@475
   537
			free(dir);
ali@475
   538
			return retval;
ali@475
   539
		} else {
ali@475
   540
			retval = malloc(3 + strlen(template) + 1);
ali@475
   541
			retval[0] = 'C';
ali@475
   542
			retval[1] = ':';
ali@475
   543
			retval[2] = '\\';
ali@475
   544
			strcpy(retval + 3, template);
ali@475
   545
		}
ali@475
   546
ali@475
   547
		free(buf);
ali@475
   548
	}
ali@475
   549
#else
ali@475
   550
	/*
ali@475
   551
	 * Find the mount point (assuming we can write to the
ali@475
   552
	 * whole filesystem). Otherwise stop at the first
ali@475
   553
	 * unwritable directory and take one step back.
ali@475
   554
	 */
ali@475
   555
	char *s, *abspath, saved;
ali@475
   556
	int len, can_step_back = 0;
ali@475
   557
	dev_t filesystem;
ali@475
   558
	struct stat buf;
ali@475
   559
ali@475
   560
	abspath = absolute_path(path);
ali@475
   561
	if (!abspath) {
ali@475
   562
		razor_set_error_posix(error, path);
ali@475
   563
		return NULL;
ali@475
   564
	}
ali@475
   565
ali@475
   566
	if (stat(abspath, &buf) < 0) {
ali@475
   567
		if (errno == ENOENT)
ali@475
   568
			filesystem = 0;
ali@475
   569
		else {
ali@475
   570
			razor_set_error_posix(error, abspath);
ali@475
   571
			free(abspath);
ali@475
   572
			return NULL;
ali@475
   573
		}
ali@475
   574
	} else
ali@475
   575
		filesystem = buf.st_dev;
ali@475
   576
ali@475
   577
	len = strlen(abspath);
ali@475
   578
	while(len > 1 && (s = strrchr(abspath, '/'))) {
ali@475
   579
		if (s == abspath) {
ali@475
   580
			saved = s[1];
ali@475
   581
			s[1] = '\0';
ali@475
   582
			len = s + 1 - abspath;
ali@475
   583
		} else {
ali@475
   584
			s[0] = '\0';
ali@475
   585
			len = s - abspath;
ali@475
   586
		}
ali@475
   587
ali@475
   588
		if (stat(abspath, &buf) < 0) {
ali@475
   589
			if (errno == ENOENT)
ali@475
   590
				continue;
ali@475
   591
			else {
ali@475
   592
			    razor_set_error_posix(error, abspath);
ali@475
   593
			    free(abspath);
ali@475
   594
			    return NULL;
ali@475
   595
			}
ali@475
   596
		} else if (!filesystem)
ali@475
   597
			filesystem = buf.st_dev;
ali@475
   598
ali@475
   599
		if (buf.st_dev != filesystem || access(abspath, W_OK)) {
ali@475
   600
			if (can_step_back) {
ali@475
   601
				if (s == abspath)
ali@475
   602
					s[1] = saved;
ali@475
   603
				else
ali@475
   604
					s[0] = '/';
ali@475
   605
			}
ali@475
   606
			len = strlen(abspath);
ali@475
   607
			break;
ali@475
   608
		} else
ali@475
   609
			can_step_back = 1;
ali@475
   610
	}
ali@475
   611
ali@475
   612
	if (len == 1)
ali@475
   613
		len = 0;	/* Avoid an unslightly double slash. */
ali@475
   614
	retval = malloc(len + 1 + strlen(template) + 1);
ali@475
   615
	memcpy(retval, abspath, len);
ali@475
   616
	retval[len] = '/';
ali@475
   617
	strcpy(retval + len + 1, template);
ali@475
   618
ali@475
   619
	free(abspath);
ali@475
   620
#endif
ali@475
   621
ali@475
   622
	if (!mkdtemp(retval)) {
ali@475
   623
		int err = errno;
ali@475
   624
ali@475
   625
#ifdef EACCES
ali@475
   626
		if (err == EACCES) {
ali@475
   627
			char *s = strdup(template);
ali@475
   628
ali@475
   629
#ifndef MSWIN_API
ali@475
   630
			if (stat(".", &buf) < 0) {
ali@475
   631
				razor_set_error_posix(error, ".");
ali@475
   632
				free(s);
ali@475
   633
				free(retval);
ali@475
   634
				return NULL;
ali@475
   635
			}
ali@475
   636
			if (buf.st_dev != filesystem)
ali@475
   637
				/*
ali@475
   638
				 * Don't use a different filesystem. It will
ali@475
   639
				 * only fail later on (in rename) and cause
ali@475
   640
				 * an unhelpful error message (EXDEV).
ali@475
   641
				 */
ali@475
   642
				free(s);
ali@475
   643
			else
ali@475
   644
#endif
ali@475
   645
			if (mkdtemp(s)) {
ali@475
   646
				free(retval);
ali@475
   647
				retval = s;
ali@475
   648
				return retval;
ali@475
   649
			} else
ali@475
   650
				free(s);
ali@475
   651
		}
ali@475
   652
#endif
ali@475
   653
ali@475
   654
		razor_set_error(error, RAZOR_POSIX_ERROR, err, retval,
ali@475
   655
				strerror(err));
ali@475
   656
ali@475
   657
		free(retval);
ali@475
   658
		retval = NULL;
ali@475
   659
	}
ali@475
   660
ali@475
   661
	return retval;
ali@475
   662
}
ali@475
   663
ali@475
   664
struct open_dir {
ali@475
   665
	uint32_t flags;
ali@475
   666
#ifdef MSWIN_API
ali@475
   667
	_WDIR *dp;
ali@475
   668
	wchar_t *path2;
ali@475
   669
#else
ali@475
   670
	DIR *dp;
ali@475
   671
	char *path;
ali@475
   672
#endif
ali@475
   673
};
ali@475
   674
ali@475
   675
#define OPEN_DIR_USED		(1U<<0)
ali@475
   676
ali@475
   677
struct array open_dirs = { 0, };
ali@475
   678
ali@475
   679
void *razor_file_opendir(const char *path, struct razor_error **error)
ali@475
   680
{
ali@475
   681
	struct open_dir *od, *odend;
ali@475
   682
ali@475
   683
	odend = open_dirs.data + open_dirs.size;
ali@475
   684
	for (od = open_dirs.data; od < odend; od++)
ali@475
   685
		if (!(od->flags & OPEN_DIR_USED))
ali@475
   686
			break;
ali@475
   687
	if (od == odend) {
ali@475
   688
		od = array_add(&open_dirs, sizeof *od);
ali@475
   689
		od->flags = 0;
ali@475
   690
	}
ali@475
   691
ali@475
   692
#ifdef MSWIN_API
ali@475
   693
	od->path2 = razor_utf8_to_utf16(path, -1);
ali@475
   694
	od->dp = _wopendir(od->path2);
ali@475
   695
#else
ali@475
   696
	od->path = strdup(path);
ali@475
   697
	od->dp = opendir(od->path);
ali@475
   698
#endif
ali@475
   699
ali@475
   700
	if (!od->dp) {
ali@475
   701
#ifdef MSWIN_API
ali@475
   702
		razor_set_error_mswin(error, od->path2, GetLastError());
ali@475
   703
		free(od->path2);
ali@475
   704
#else
ali@475
   705
		razor_set_error_posix(error, od->path);
ali@475
   706
		free(od->path);
ali@475
   707
#endif
ali@475
   708
		return NULL;
ali@475
   709
	}
ali@475
   710
ali@475
   711
	od->flags = OPEN_DIR_USED;
ali@475
   712
	return od;
ali@475
   713
}
ali@475
   714
ali@475
   715
char *razor_file_readdir(void *dp, struct razor_error **error)
ali@475
   716
{
ali@475
   717
	struct open_dir *od = dp, *odend;
ali@475
   718
#ifdef MSWIN_API
ali@475
   719
	struct _wdirent *dirp;
ali@475
   720
	char *path;
ali@475
   721
#else
ali@475
   722
	struct dirent *dirp;
ali@475
   723
#endif
ali@475
   724
ali@475
   725
	odend = open_dirs.data + open_dirs.size;
ali@475
   726
	if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
ali@475
   727
		return (char *)-1;
ali@475
   728
ali@475
   729
	errno = 0;
ali@475
   730
ali@475
   731
#ifdef MSWIN_API
ali@475
   732
	while((dirp = _wreaddir(od->dp))) {
ali@475
   733
		path = razor_utf16_to_utf8(dirp->d_name, -1);
ali@475
   734
		if (strcmp(path, ".") && strcmp(path, ".."))
ali@475
   735
			return path;
ali@475
   736
		else
ali@475
   737
			free(path);
ali@475
   738
	}
ali@475
   739
#else
ali@475
   740
	while((dirp = readdir(od->dp)))
ali@475
   741
		if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, ".."))
ali@475
   742
			return strdup(dirp->d_name);
ali@475
   743
#endif
ali@475
   744
ali@475
   745
	if (errno) {
ali@475
   746
#ifdef MSWIN_API
ali@475
   747
		razor_set_error_mswin(error, od->path2, GetLastError());
ali@475
   748
#else
ali@475
   749
		razor_set_error_posix(error, od->path);
ali@475
   750
#endif
ali@475
   751
	}
ali@475
   752
ali@475
   753
	return NULL;
ali@475
   754
}
ali@475
   755
ali@475
   756
int razor_file_closedir(void *dp, struct razor_error **error)
ali@475
   757
{
ali@475
   758
	struct open_dir *od = dp, *odend;
ali@475
   759
	int retval;
ali@475
   760
ali@475
   761
	odend = open_dirs.data + open_dirs.size;
ali@475
   762
	if (dp < open_dirs.data || od >= odend || !(od->flags & OPEN_DIR_USED))
ali@475
   763
		return -2;
ali@475
   764
ali@475
   765
#ifdef MSWIN_API
ali@475
   766
	/*
ali@475
   767
	 * I can't find documentation to state that _wclosedir()
ali@475
   768
	 * returns -1 on failure, so be paranoid.
ali@475
   769
	 */
ali@475
   770
	retval = _wclosedir(od->dp) ? -1 : 0;
ali@475
   771
#else
ali@475
   772
	retval = closedir(od->dp);
ali@475
   773
#endif
ali@475
   774
ali@475
   775
	if (retval) {
ali@475
   776
#ifdef MSWIN_API
ali@475
   777
		razor_set_error_mswin(error, od->path2, GetLastError());
ali@475
   778
#else
ali@475
   779
		razor_set_error_posix(error, od->path);
ali@475
   780
#endif
ali@475
   781
	}
ali@475
   782
ali@475
   783
#ifdef MSWIN_API
ali@475
   784
	free(od->path2);
ali@475
   785
#else
ali@475
   786
	free(od->path);
ali@475
   787
#endif
ali@475
   788
ali@475
   789
	od->flags &= ~OPEN_DIR_USED;
ali@475
   790
ali@475
   791
	for (od = open_dirs.data; od < odend; od++)
ali@475
   792
		if (od->flags & OPEN_DIR_USED)
ali@475
   793
			break;
ali@475
   794
ali@475
   795
	if (od == odend) {
ali@475
   796
		array_release(&open_dirs);
ali@475
   797
		array_init(&open_dirs);
ali@475
   798
	}
ali@475
   799
ali@475
   800
	return retval;
ali@475
   801
}
ali@475
   802
ali@475
   803
struct razor_uri_vtable_entry {
ali@475
   804
	struct razor_uri_vtable vtable;
ali@475
   805
	char *scheme;
ali@475
   806
	struct array open_files, open_directories;
ali@475
   807
};
ali@475
   808
ali@475
   809
static struct array razor_uri_vtable_entries;
ali@475
   810
ali@475
   811
static struct razor_uri_vtable_entry *
ali@475
   812
  razor_uri_get_vtable_entry(const char *scheme)
ali@475
   813
{
ali@475
   814
	struct razor_uri_vtable_entry *entry, *eend, *fallback = NULL;
ali@475
   815
ali@475
   816
	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
ali@475
   817
	for(entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
ali@475
   818
		if (!strcmp0(entry->scheme, scheme))
ali@475
   819
			return entry;
ali@475
   820
		else if (!entry->scheme)
ali@475
   821
			fallback = entry;
ali@475
   822
	}
ali@475
   823
ali@475
   824
	return fallback;
ali@475
   825
}
ali@475
   826
ali@476
   827
RAZOR_EXPORT int
ali@476
   828
razor_uri_mkdir(const char *uri, mode_t mode, struct razor_error **error)
ali@475
   829
{
ali@475
   830
	int retval;
ali@475
   831
	char *path;
ali@475
   832
	struct razor_uri ru;
ali@475
   833
	struct razor_uri_vtable_entry *entry;
ali@475
   834
	struct razor_error *tmp_error = NULL;
ali@475
   835
ali@475
   836
	if (razor_uri_parse(&ru, uri, error))
ali@475
   837
		return -1;
ali@475
   838
ali@475
   839
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
   840
ali@475
   841
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
   842
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
   843
		razor_error_free(tmp_error);
ali@475
   844
	else if (!path) {
ali@475
   845
		razor_propagate_error(error, tmp_error, NULL);
ali@475
   846
		razor_uri_destroy(&ru);
ali@475
   847
		return -1;
ali@475
   848
	}
ali@475
   849
ali@475
   850
	if (path) {
ali@475
   851
		razor_uri_destroy(&ru);
ali@475
   852
		retval = razor_file_mkdir(path, mode, error);
ali@475
   853
		free(path);
ali@475
   854
	} else {
ali@475
   855
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
   856
		razor_uri_destroy(&ru);
ali@475
   857
		if (!entry || !entry->vtable.mkdir) {
ali@475
   858
			retval = -1;
ali@475
   859
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
   860
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
   861
					uri, "No URI handler installed");
ali@475
   862
		} else
ali@475
   863
			retval = entry->vtable.mkdir(uri, mode, error);
ali@475
   864
	}
ali@475
   865
ali@475
   866
	return retval;
ali@475
   867
}
ali@475
   868
ali@476
   869
RAZOR_EXPORT int
ali@476
   870
razor_uri_unlink(const char *uri, struct razor_error **error)
ali@475
   871
{
ali@475
   872
	int retval;
ali@475
   873
	char *path;
ali@475
   874
	struct razor_uri ru;
ali@475
   875
	struct razor_uri_vtable_entry *entry;
ali@475
   876
	struct razor_error *tmp_error = NULL;
ali@475
   877
ali@475
   878
	if (razor_uri_parse(&ru, uri, error))
ali@475
   879
		return -1;
ali@475
   880
ali@475
   881
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
   882
ali@475
   883
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
   884
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
   885
		razor_error_free(tmp_error);
ali@475
   886
	else if (!path) {
ali@475
   887
		razor_propagate_error(error, tmp_error, NULL);
ali@475
   888
		razor_uri_destroy(&ru);
ali@475
   889
		return -1;
ali@475
   890
	}
ali@475
   891
ali@475
   892
	if (path) {
ali@475
   893
		razor_uri_destroy(&ru);
ali@475
   894
		retval = razor_file_unlink(path, error);
ali@475
   895
		free(path);
ali@475
   896
	} else {
ali@475
   897
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
   898
		razor_uri_destroy(&ru);
ali@475
   899
		if (!entry || !entry->vtable.unlink) {
ali@475
   900
			retval = -1;
ali@475
   901
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
   902
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
   903
					uri, "No URI handler installed");
ali@475
   904
		} else
ali@475
   905
			retval = entry->vtable.unlink(uri, error);
ali@475
   906
	}
ali@475
   907
ali@475
   908
	return retval;
ali@475
   909
}
ali@475
   910
ali@476
   911
RAZOR_EXPORT int
ali@476
   912
razor_uri_open(const char *uri, int flags, mode_t mode,
ali@476
   913
	       struct razor_error **error)
ali@475
   914
{
ali@475
   915
	int retval;
ali@475
   916
	char *path;
ali@475
   917
	struct razor_uri ru;
ali@475
   918
	struct razor_uri_vtable_entry *entry;
ali@475
   919
	struct razor_error *tmp_error = NULL;
ali@475
   920
ali@475
   921
	if (razor_uri_parse(&ru, uri, error))
ali@475
   922
		return -1;
ali@475
   923
ali@475
   924
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
   925
ali@475
   926
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
   927
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
   928
		razor_error_free(tmp_error);
ali@475
   929
	else if (!path) {
ali@475
   930
		razor_propagate_error(error, tmp_error, NULL);
ali@475
   931
		razor_uri_destroy(&ru);
ali@475
   932
		return -1;
ali@475
   933
	}
ali@475
   934
ali@475
   935
	if (path) {
ali@475
   936
		razor_uri_destroy(&ru);
ali@475
   937
		retval = razor_file_open(path, flags, mode, error);
ali@475
   938
		free(path);
ali@475
   939
	} else {
ali@475
   940
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
   941
		razor_uri_destroy(&ru);
ali@475
   942
		if (!entry || !entry->vtable.open) {
ali@475
   943
			retval = -1;
ali@475
   944
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
   945
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
   946
					uri, "No URI handler installed");
ali@475
   947
		} else
ali@475
   948
			retval = entry->vtable.open(uri, flags, mode, error);
ali@475
   949
	}
ali@475
   950
ali@475
   951
	return retval;
ali@475
   952
}
ali@475
   953
ali@476
   954
RAZOR_EXPORT int
ali@476
   955
razor_uri_move(const char *src_uri, const char *dst_uri,
ali@476
   956
	       struct razor_error **error)
ali@475
   957
{
ali@475
   958
	int retval;
ali@475
   959
	char *src_path, *dst_path;
ali@475
   960
	struct razor_uri src_ru, dst_ru;
ali@475
   961
	struct razor_uri_vtable_entry *entry;
ali@475
   962
	struct razor_error *tmp_error = NULL;
ali@475
   963
ali@475
   964
	if (razor_uri_parse(&src_ru, src_uri, error) ||
ali@475
   965
	    razor_uri_parse(&dst_ru, dst_uri, error))
ali@475
   966
		return -1;
ali@475
   967
ali@475
   968
	src_path = razor_path_from_parsed_uri(&src_ru, &tmp_error);
ali@475
   969
ali@475
   970
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
   971
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
   972
		razor_error_free(tmp_error);
ali@475
   973
	else if (!src_path) {
ali@475
   974
		razor_propagate_error(error, tmp_error, NULL);
ali@475
   975
		razor_uri_destroy(&src_ru);
ali@475
   976
		return -1;
ali@475
   977
	}
ali@475
   978
ali@475
   979
	dst_path = razor_path_from_parsed_uri(&dst_ru, &tmp_error);
ali@475
   980
ali@475
   981
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
   982
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
   983
		razor_error_free(tmp_error);
ali@475
   984
	else if (!dst_path) {
ali@475
   985
		razor_propagate_error(error, tmp_error, NULL);
ali@475
   986
		razor_uri_destroy(&dst_ru);
ali@475
   987
		razor_uri_destroy(&src_ru);
ali@475
   988
		free(src_path);
ali@475
   989
		return -1;
ali@475
   990
	}
ali@475
   991
ali@475
   992
	if (src_path && dst_path)
ali@475
   993
		retval = razor_file_move(src_path, dst_path, error);
ali@475
   994
	else {
ali@475
   995
		if (!strcmp(src_ru.scheme, dst_ru.scheme))
ali@475
   996
			entry = razor_uri_get_vtable_entry(src_ru.scheme);
ali@475
   997
		else
ali@475
   998
			entry = NULL;
ali@475
   999
		if (entry && entry->vtable.move)
ali@475
  1000
			retval = entry->vtable.move(src_uri, dst_uri, error);
ali@475
  1001
		else if (strcmp(src_ru.scheme, dst_ru.scheme)) {
ali@475
  1002
			retval = -1;
ali@475
  1003
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1004
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
  1005
					dst_uri, "Cross-scheme URI move");
ali@475
  1006
		} else {
ali@475
  1007
			retval = -1;
ali@475
  1008
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1009
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
  1010
					src_path ? dst_uri : src_uri,
ali@475
  1011
					"No URI handler installed");
ali@475
  1012
		}
ali@475
  1013
	}
ali@475
  1014
ali@475
  1015
	razor_uri_destroy(&src_ru);
ali@475
  1016
	razor_uri_destroy(&dst_ru);
ali@475
  1017
	free(src_path);
ali@475
  1018
	free(dst_path);
ali@475
  1019
ali@475
  1020
	return retval;
ali@475
  1021
}
ali@475
  1022
ali@476
  1023
RAZOR_EXPORT void *
ali@476
  1024
razor_uri_get_contents(const char *uri, size_t *length, int private,
ali@476
  1025
		       struct razor_error **error)
ali@475
  1026
{
ali@475
  1027
	void *retval;
ali@475
  1028
	char *path;
ali@475
  1029
	struct razor_uri ru;
ali@475
  1030
	struct razor_uri_vtable_entry *entry;
ali@475
  1031
	struct razor_error *tmp_error = NULL;
ali@475
  1032
ali@475
  1033
	if (razor_uri_parse(&ru, uri, error))
ali@475
  1034
		return NULL;
ali@475
  1035
ali@475
  1036
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
  1037
ali@475
  1038
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
  1039
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
  1040
		razor_error_free(tmp_error);
ali@475
  1041
	else if (!path) {
ali@475
  1042
		razor_propagate_error(error, tmp_error, NULL);
ali@475
  1043
		razor_uri_destroy(&ru);
ali@475
  1044
		return NULL;
ali@475
  1045
	}
ali@475
  1046
ali@475
  1047
	if (path) {
ali@475
  1048
		razor_uri_destroy(&ru);
ali@475
  1049
		retval = razor_file_get_contents(path, length, private, error);
ali@475
  1050
		free(path);
ali@475
  1051
	} else {
ali@475
  1052
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
  1053
		razor_uri_destroy(&ru);
ali@475
  1054
		if (!entry || !entry->vtable.get_contents) {
ali@475
  1055
			retval = NULL;
ali@475
  1056
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1057
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
  1058
					uri, "No URI handler installed");
ali@475
  1059
		} else {
ali@475
  1060
			retval = entry->vtable.get_contents(uri, length,
ali@475
  1061
							    private, error);
ali@475
  1062
			if (retval)
ali@475
  1063
				ptr_array_add(&entry->open_files, retval);
ali@475
  1064
		}
ali@475
  1065
	}
ali@475
  1066
ali@475
  1067
	return retval;
ali@475
  1068
}
ali@475
  1069
ali@476
  1070
RAZOR_EXPORT int razor_uri_free_contents(void *addr, size_t length)
ali@475
  1071
{
ali@475
  1072
	int retval, of;
ali@475
  1073
	struct razor_uri_vtable_entry *entry, *eend;
ali@475
  1074
ali@475
  1075
	if (!addr)
ali@475
  1076
		return 0;
ali@475
  1077
ali@475
  1078
	retval = razor_file_free_contents(addr, length);
ali@475
  1079
ali@475
  1080
	if (retval != -2)
ali@475
  1081
		return retval;
ali@475
  1082
ali@475
  1083
	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
ali@475
  1084
	for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
ali@475
  1085
		of = ptr_array_find(&entry->open_files, addr);
ali@475
  1086
		if (of >= 0) {
ali@475
  1087
			if (entry->vtable.free_contents)
ali@475
  1088
				retval = entry->vtable.free_contents(addr,
ali@475
  1089
								     length);
ali@475
  1090
			ptr_array_remove_index(&entry->open_files, of);
ali@475
  1091
			break;
ali@475
  1092
		}
ali@475
  1093
	}
ali@475
  1094
ali@475
  1095
	return retval;
ali@475
  1096
}
ali@475
  1097
ali@476
  1098
RAZOR_EXPORT int
ali@476
  1099
razor_uri_is_directory(const char *uri, struct razor_error **error)
ali@475
  1100
{
ali@475
  1101
	int retval;
ali@475
  1102
	char *path;
ali@475
  1103
	struct razor_uri ru;
ali@475
  1104
	struct razor_uri_vtable_entry *entry;
ali@475
  1105
	struct razor_error *tmp_error = NULL;
ali@475
  1106
ali@475
  1107
	if (razor_uri_parse(&ru, uri, error))
ali@475
  1108
		return -1;
ali@475
  1109
ali@475
  1110
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
  1111
ali@475
  1112
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
  1113
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
  1114
		razor_error_free(tmp_error);
ali@475
  1115
	else if (!path) {
ali@475
  1116
		razor_propagate_error(error, tmp_error, NULL);
ali@475
  1117
		razor_uri_destroy(&ru);
ali@475
  1118
		return -1;
ali@475
  1119
	}
ali@475
  1120
ali@475
  1121
	if (path) {
ali@475
  1122
		razor_uri_destroy(&ru);
ali@475
  1123
		retval = razor_file_is_directory(path, error);
ali@475
  1124
		free(path);
ali@475
  1125
	} else {
ali@475
  1126
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
  1127
		razor_uri_destroy(&ru);
ali@475
  1128
		if (!entry || !entry->vtable.is_directory) {
ali@475
  1129
			retval = -1;
ali@475
  1130
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1131
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
  1132
					uri, "No URI handler installed");
ali@475
  1133
		} else
ali@475
  1134
			retval = entry->vtable.is_directory(uri, error);
ali@475
  1135
	}
ali@475
  1136
ali@475
  1137
	return retval;
ali@475
  1138
}
ali@475
  1139
ali@476
  1140
RAZOR_EXPORT char *
ali@476
  1141
razor_uri_mkdtemp_near(const char *uri, const char *template,
ali@476
  1142
		       struct razor_error **error)
ali@475
  1143
{
ali@475
  1144
	char *retval, *s;
ali@475
  1145
	char *path;
ali@475
  1146
	struct razor_uri ru;
ali@475
  1147
	struct razor_uri_vtable_entry *entry;
ali@475
  1148
	struct razor_error *tmp_error = NULL;
ali@475
  1149
ali@475
  1150
	if (razor_uri_parse(&ru, uri, error))
ali@475
  1151
		return NULL;
ali@475
  1152
ali@475
  1153
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
  1154
ali@475
  1155
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
  1156
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
  1157
		razor_error_free(tmp_error);
ali@475
  1158
	else if (!path) {
ali@475
  1159
		razor_propagate_error(error, tmp_error, NULL);
ali@475
  1160
		razor_uri_destroy(&ru);
ali@475
  1161
		return NULL;
ali@475
  1162
	}
ali@475
  1163
ali@475
  1164
	if (path) {
ali@475
  1165
		razor_uri_destroy(&ru);
ali@475
  1166
		s = razor_file_mkdtemp_near(path, template, error);
ali@479
  1167
		if (s) {
ali@479
  1168
			retval = razor_path_to_uri(s);
ali@479
  1169
			free(s);
ali@479
  1170
		} else
ali@479
  1171
			retval = NULL;
ali@475
  1172
		free(path);
ali@475
  1173
	} else {
ali@475
  1174
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
  1175
		razor_uri_destroy(&ru);
ali@475
  1176
		if (!entry || !entry->vtable.mkdtemp_near) {
ali@475
  1177
			retval = NULL;
ali@475
  1178
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1179
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
  1180
					uri, "No URI handler installed");
ali@475
  1181
		} else
ali@475
  1182
			retval = entry->vtable.mkdtemp_near(uri, template,
ali@475
  1183
							    error);
ali@475
  1184
	}
ali@475
  1185
ali@475
  1186
	return retval;
ali@475
  1187
}
ali@475
  1188
ali@476
  1189
RAZOR_EXPORT void *
ali@476
  1190
razor_uri_opendir(const char *uri, struct razor_error **error)
ali@475
  1191
{
ali@475
  1192
	void *retval;
ali@475
  1193
	char *path;
ali@475
  1194
	struct razor_uri ru;
ali@475
  1195
	struct razor_uri_vtable_entry *entry;
ali@475
  1196
	struct razor_error *tmp_error = NULL;
ali@475
  1197
ali@475
  1198
	if (razor_uri_parse(&ru, uri, error))
ali@475
  1199
		return NULL;
ali@475
  1200
ali@475
  1201
	path = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@475
  1202
ali@475
  1203
	if (razor_error_matches(tmp_error, RAZOR_GENERAL_ERROR,
ali@475
  1204
	    RAZOR_GENERAL_ERROR_UNSUPPORTED_URI))
ali@475
  1205
		razor_error_free(tmp_error);
ali@475
  1206
	else if (!path) {
ali@475
  1207
		razor_propagate_error(error, tmp_error, NULL);
ali@475
  1208
		razor_uri_destroy(&ru);
ali@475
  1209
		return NULL;
ali@475
  1210
	}
ali@475
  1211
ali@475
  1212
	if (path) {
ali@475
  1213
		razor_uri_destroy(&ru);
ali@475
  1214
		retval = razor_file_opendir(path, error);
ali@475
  1215
		free(path);
ali@475
  1216
	} else {
ali@475
  1217
		entry = razor_uri_get_vtable_entry(ru.scheme);
ali@475
  1218
		razor_uri_destroy(&ru);
ali@475
  1219
		if (!entry || !entry->vtable.opendir) {
ali@475
  1220
			retval = NULL;
ali@475
  1221
			razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1222
					RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
ali@475
  1223
					uri, "No URI handler installed");
ali@475
  1224
		} else {
ali@475
  1225
			retval = entry->vtable.opendir(uri, error);
ali@475
  1226
			if (retval)
ali@475
  1227
				ptr_array_add(&entry->open_directories, retval);
ali@475
  1228
		}
ali@475
  1229
	}
ali@475
  1230
ali@475
  1231
	return retval;
ali@475
  1232
}
ali@475
  1233
ali@476
  1234
RAZOR_EXPORT char *razor_uri_readdir(void *dir, struct razor_error **error)
ali@475
  1235
{
ali@475
  1236
	int od;
ali@475
  1237
	char *retval;
ali@475
  1238
	struct razor_uri_vtable_entry *entry, *eend;
ali@475
  1239
ali@475
  1240
	retval = razor_file_readdir(dir, error);
ali@475
  1241
ali@475
  1242
	if (retval != (char *)-1)
ali@475
  1243
		return retval;
ali@475
  1244
ali@475
  1245
	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
ali@475
  1246
	for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
ali@475
  1247
		od = ptr_array_find(&entry->open_directories, dir);
ali@475
  1248
		if (od >= 0) {
ali@475
  1249
			if (entry->vtable.readdir)
ali@475
  1250
				retval = entry->vtable.readdir(dir, error);
ali@475
  1251
			break;
ali@475
  1252
		}
ali@475
  1253
	}
ali@475
  1254
ali@475
  1255
	if (retval == (char *)-1) {
ali@475
  1256
		retval = NULL;
ali@475
  1257
		razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1258
				RAZOR_GENERAL_ERROR_FAILED, NULL,
ali@475
  1259
				"Invalid directory handle");
ali@475
  1260
	}
ali@475
  1261
ali@475
  1262
	return retval;
ali@475
  1263
}
ali@475
  1264
ali@476
  1265
RAZOR_EXPORT int razor_uri_closedir(void *dir, struct razor_error **error)
ali@475
  1266
{
ali@475
  1267
	int od;
ali@475
  1268
	int retval;
ali@475
  1269
	struct razor_uri_vtable_entry *entry, *eend;
ali@475
  1270
ali@475
  1271
	retval = razor_file_closedir(dir, error);
ali@475
  1272
ali@475
  1273
	if (retval != -2)
ali@475
  1274
		return retval;
ali@475
  1275
ali@475
  1276
	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
ali@475
  1277
	for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
ali@475
  1278
		od = ptr_array_find(&entry->open_directories, dir);
ali@475
  1279
		if (od >= 0) {
ali@475
  1280
			if (entry->vtable.closedir)
ali@475
  1281
				retval = entry->vtable.closedir(dir, error);
ali@475
  1282
			break;
ali@475
  1283
		}
ali@475
  1284
	}
ali@475
  1285
ali@475
  1286
	if (retval == -2)
ali@475
  1287
		razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1288
				RAZOR_GENERAL_ERROR_FAILED, NULL,
ali@475
  1289
				"Invalid directory handle");
ali@475
  1290
ali@475
  1291
	return retval;
ali@475
  1292
}
ali@475
  1293
ali@475
  1294
RAZOR_EXPORT int razor_uri_set_vtable(const char *scheme,
ali@475
  1295
  struct razor_uri_vtable *vtable, struct razor_error **error)
ali@475
  1296
{
ali@475
  1297
	struct razor_uri_vtable_entry *entry, *eend;
ali@475
  1298
ali@475
  1299
	if (!strcmp0(scheme, "file")) {
ali@475
  1300
		/*
ali@475
  1301
		 * There's no fundamental reason why we couldn't support
ali@475
  1302
		 * this, but it's a lot of work without any obvious need.
ali@475
  1303
		 */
ali@475
  1304
		razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1305
				RAZOR_GENERAL_ERROR_FAILED, scheme,
ali@475
  1306
				"Can't override file scheme handler");
ali@475
  1307
		return -1;
ali@475
  1308
	}
ali@475
  1309
ali@475
  1310
	if (vtable->structure_size < sizeof(struct razor_uri_vtable)) {
ali@475
  1311
		/*
ali@475
  1312
		 * A future version of the API might add vfuncs to the
ali@475
  1313
		 * table (which we ignore), but won't remove any.
ali@475
  1314
		 */
ali@475
  1315
		razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1316
				RAZOR_GENERAL_ERROR_FAILED, scheme,
ali@475
  1317
				"Invalid vtable structure size");
ali@475
  1318
		return -1;
ali@475
  1319
	}
ali@475
  1320
ali@475
  1321
	eend = razor_uri_vtable_entries.data + razor_uri_vtable_entries.size;
ali@475
  1322
	for (entry = razor_uri_vtable_entries.data; entry < eend; entry++) {
ali@475
  1323
		if (!strcmp0(entry->scheme, scheme))
ali@475
  1324
			break;
ali@475
  1325
	}
ali@475
  1326
ali@475
  1327
	if (entry == eend) {
ali@475
  1328
		if (!vtable)
ali@475
  1329
			return 0;
ali@475
  1330
		entry = array_add(&razor_uri_vtable_entries, sizeof *entry);
ali@475
  1331
		entry->scheme = scheme ? strdup(scheme) : NULL;
ali@475
  1332
		array_init(&entry->open_files);
ali@475
  1333
		array_init(&entry->open_directories);
ali@475
  1334
	} else if (entry->open_files.size || entry->open_directories.size) {
ali@475
  1335
		razor_set_error(error, RAZOR_GENERAL_ERROR,
ali@475
  1336
				RAZOR_GENERAL_ERROR_FAILED, scheme,
ali@475
  1337
				"URI handler busy");
ali@475
  1338
		return -1;
ali@475
  1339
	}
ali@475
  1340
ali@475
  1341
	if (vtable) {
ali@475
  1342
		entry->vtable = *vtable;
ali@475
  1343
		entry->vtable.structure_size = sizeof(struct razor_uri_vtable);
ali@475
  1344
	} else {
ali@475
  1345
		free(entry->scheme);
ali@475
  1346
		if (entry + 1 < eend)
ali@475
  1347
			memmove(entry, entry + 1, eend - (entry + 1));
ali@475
  1348
		array_set_size(&razor_uri_vtable_entries,
ali@475
  1349
			       razor_uri_vtable_entries.size - sizeof *entry);
ali@475
  1350
	}
ali@475
  1351
ali@475
  1352
	return 0;
ali@475
  1353
}