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