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