librazor/util.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Apr 23 19:04:43 2010 +0100 (2010-04-23)
changeset 392 ad1aed244c10
parent 372 6e93e5485947
child 403 e63951c1d0f8
permissions -rw-r--r--
Start 0.4
richard@300
     1
/*
richard@300
     2
 * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
richard@300
     3
 * Copyright (C) 2008  Red Hat, Inc
ali@322
     4
 * Copyright (C) 2009  J. Ali Harlow <ali@juiblex.co.uk>
richard@300
     5
 *
richard@300
     6
 * This program is free software; you can redistribute it and/or modify
richard@300
     7
 * it under the terms of the GNU General Public License as published by
richard@300
     8
 * the Free Software Foundation; either version 2 of the License, or
richard@300
     9
 * (at your option) any later version.
richard@300
    10
 *
richard@300
    11
 * This program is distributed in the hope that it will be useful,
richard@300
    12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
richard@300
    13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
richard@300
    14
 * GNU General Public License for more details.
richard@300
    15
 *
richard@300
    16
 * You should have received a copy of the GNU General Public License along
richard@300
    17
 * with this program; if not, write to the Free Software Foundation, Inc.,
richard@300
    18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
richard@300
    19
 */
richard@300
    20
ali@322
    21
#include "config.h"
ali@322
    22
rhughes@241
    23
#include <limits.h>
rhughes@241
    24
#include <string.h>
ali@322
    25
#include <sys/types.h>
rhughes@241
    26
#include <sys/stat.h>
rhughes@241
    27
#include <stdlib.h>
rhughes@241
    28
#include <stdio.h>
rhughes@241
    29
#include <stdint.h>
ali@339
    30
#include <errno.h>
rhughes@241
    31
#include <unistd.h>
ali@322
    32
#include <fcntl.h>
ali@359
    33
#ifdef MSWIN_API
ali@377
    34
#include <windows.h>
ali@359
    35
#include <direct.h>
ali@359
    36
#endif
ali@322
    37
#if HAVE_SYS_MMAN_H
ali@322
    38
#include <sys/mman.h>
ali@322
    39
#endif
ali@372
    40
#include <assert.h>
rhughes@241
    41
ali@359
    42
#include "razor.h"
rhughes@241
    43
#include "razor-internal.h"
rhughes@241
    44
ali@322
    45
#ifndef O_BINARY
ali@322
    46
#define O_BINARY	0
ali@322
    47
#endif
ali@322
    48
ali@359
    49
#define RAZOR_ASCII_ISALPHA(c)	\
ali@359
    50
			((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z')
ali@359
    51
ali@338
    52
/* Required by gnulib on non-libc platforms */
ali@338
    53
char *program_name = "librazor";
ali@338
    54
ali@359
    55
static int allow_all_root_names = 0;
ali@359
    56
ali@359
    57
/*
ali@359
    58
 * Primarily intended for testing named roots under UNIX platforms.
ali@359
    59
 */
ali@359
    60
RAZOR_EXPORT void razor_disable_root_name_checks(int disable)
ali@359
    61
{
ali@359
    62
	allow_all_root_names = disable;
ali@359
    63
}
ali@359
    64
ali@359
    65
static int razor_valid_root_name(const char *name)
ali@359
    66
{
ali@359
    67
	if (allow_all_root_names)
ali@359
    68
		return !strchr(name,'/');
ali@359
    69
ali@359
    70
#ifdef MSWIN_API
ali@359
    71
	return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
ali@359
    72
	       name[2] == '\0';
ali@359
    73
#else
ali@359
    74
	return name[0] == '\0';
ali@359
    75
#endif
ali@359
    76
}
ali@359
    77
rhughes@241
    78
int
rhughes@241
    79
razor_create_dir(const char *root, const char *path)
rhughes@241
    80
{
rhughes@241
    81
	char buffer[PATH_MAX], *p;
rhughes@241
    82
	const char *slash, *next;
rhughes@241
    83
	struct stat buf;
rhughes@241
    84
rhughes@241
    85
	/* Create all sub-directories in dir. We know root exists and
ali@359
    86
	 * is a dir, root does not end in a '/', and path either has a
ali@359
    87
	 * leading '/' or (on MS-Windows only) root is the empty string
ali@359
    88
	 * and path starts with drive (eg., "c:/windows"). */
rhughes@241
    89
rhughes@241
    90
	strcpy(buffer, root);
rhughes@241
    91
	p = buffer + strlen(buffer);
rhughes@241
    92
	slash = path;
rhughes@241
    93
	for (slash = path; *slash != '\0'; slash = next) {
rhughes@241
    94
		next = strchr(slash + 1, '/');
rhughes@241
    95
		if (next == NULL)
rhughes@241
    96
			break;
rhughes@241
    97
rhughes@241
    98
		memcpy(p, slash, next - slash);
rhughes@241
    99
		p += next - slash;
rhughes@241
   100
		*p = '\0';
rhughes@241
   101
ali@359
   102
		if (razor_valid_root_name(buffer))
ali@359
   103
			continue;
ali@359
   104
rhughes@241
   105
		if (stat(buffer, &buf) == 0) {
rhughes@241
   106
			if (!S_ISDIR(buf.st_mode)) {
rhughes@241
   107
				fprintf(stderr,
rhughes@241
   108
					"%s exists but is not a directory\n",
rhughes@241
   109
					buffer);
rhughes@241
   110
				return -1;
rhughes@241
   111
			}
rhughes@241
   112
		} else if (mkdir(buffer, 0777) < 0) {
ali@339
   113
			fprintf(stderr, "failed to make directory %s: %s\n",
ali@339
   114
				buffer, strerror(errno));
rhughes@241
   115
			return -1;
rhughes@241
   116
		}
rhughes@241
   117
rhughes@241
   118
		/* FIXME: What to do about permissions for dirs we
rhughes@241
   119
		 * have to create but are not in the cpio archive? */
rhughes@241
   120
	}
rhughes@241
   121
rhughes@241
   122
	return 0;
rhughes@241
   123
}
rhughes@241
   124
rhughes@241
   125
int
ali@377
   126
razor_remove(const char *path)
ali@377
   127
{
ali@377
   128
#ifdef MSWIN_API
ali@377
   129
	DWORD err;
ali@377
   130
ali@377
   131
	if (DeleteFile(path))
ali@377
   132
		return 0;
ali@377
   133
ali@377
   134
	err = GetLastError();
ali@377
   135
	if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
ali@377
   136
		return 0;
ali@377
   137
ali@377
   138
	if (SetFileAttributes(path, FILE_ATTRIBUTE_NORMAL) && DeleteFile(path))
ali@377
   139
		return 0;
ali@377
   140
ali@377
   141
	if (RemoveDirectory(path) || GetLastError() == ERROR_DIR_NOT_EMPTY)
ali@377
   142
		return 0;
ali@377
   143
ali@377
   144
	/*
ali@377
   145
	 * It would be tempting to use:
ali@377
   146
	 * 	MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)
ali@377
   147
	 * but unless we can guarantee that the system will be rebooted
ali@377
   148
	 * before we (or some other application) write another file with the
ali@377
   149
	 * same path, this is likely to cause more problems than it solves.
ali@377
   150
	 */
ali@377
   151
ali@377
   152
	/* Use remove() as a fallback so that errno is set appropriately */
ali@377
   153
#endif
ali@377
   154
ali@377
   155
	return remove(path);
ali@377
   156
}
ali@377
   157
ali@377
   158
int
rhughes@241
   159
razor_write(int fd, const void *data, size_t size)
rhughes@241
   160
{
rhughes@241
   161
	size_t rest;
rhughes@241
   162
	ssize_t written;
rhughes@241
   163
	const unsigned char *p;
rhughes@241
   164
rhughes@241
   165
	rest = size;
rhughes@241
   166
	p = data;
rhughes@241
   167
	while (rest > 0) {
rhughes@241
   168
		written = write(fd, p, rest);
rhughes@241
   169
		if (written < 0) {
ali@339
   170
			perror("write error");
rhughes@241
   171
			return -1;
rhughes@241
   172
		}
rhughes@241
   173
		rest -= written;
rhughes@241
   174
		p += written;
rhughes@241
   175
	}
rhughes@241
   176
rhughes@241
   177
	return 0;
rhughes@241
   178
}
rhughes@241
   179
ali@322
   180
void *
ali@322
   181
razor_file_get_contents(const char *filename, size_t *length)
ali@322
   182
{
ali@322
   183
	int fd;
ali@322
   184
	struct stat st;
ali@322
   185
	void *addr;
ali@322
   186
#if !HAVE_SYS_MMAN_H
ali@322
   187
	size_t nb;
ali@322
   188
	ssize_t res;
ali@322
   189
#endif
ali@322
   190
ali@322
   191
	fd = open(filename, O_RDONLY | O_BINARY);
ali@322
   192
	if (fd < 0)
ali@322
   193
		return NULL;
ali@322
   194
ali@322
   195
	if (fstat(fd, &st) < 0) {
ali@322
   196
		close(fd);
ali@322
   197
		return NULL;
ali@322
   198
	}
ali@322
   199
ali@322
   200
	*length = st.st_size;
ali@322
   201
#if HAVE_SYS_MMAN_H
ali@322
   202
	addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
ali@322
   203
#else
ali@322
   204
	addr = malloc(st.st_size);
ali@322
   205
	if (addr) {
ali@322
   206
		nb = 0;
ali@322
   207
		while(nb < st.st_size) {
ali@322
   208
			res = read(fd, addr + nb, st.st_size - nb);
ali@322
   209
			if (res <= 0) {
ali@322
   210
				free(addr);
ali@322
   211
				addr = NULL;
ali@322
   212
				break;
ali@322
   213
			}
ali@322
   214
			nb += res;
ali@322
   215
		}
ali@322
   216
	}
ali@322
   217
#endif
ali@322
   218
	close(fd);
ali@322
   219
ali@322
   220
#if HAVE_SYS_MMAN_H
ali@322
   221
	if (addr == MAP_FAILED)
ali@322
   222
		addr = NULL;
ali@322
   223
#endif
ali@322
   224
ali@322
   225
	return addr;
ali@322
   226
}
ali@322
   227
ali@322
   228
int
ali@322
   229
razor_file_free_contents(void *addr, size_t length)
ali@322
   230
{
ali@322
   231
#if HAVE_SYS_MMAN_H
ali@322
   232
	return munmap(addr, length);
ali@322
   233
#else
ali@322
   234
	free(addr);
ali@322
   235
	return 0;
ali@322
   236
#endif
ali@322
   237
}
ali@322
   238
rhughes@241
   239
struct qsort_context {
rhughes@241
   240
	size_t size;
rhughes@241
   241
	razor_compare_with_data_func_t compare;
rhughes@241
   242
	void *data;
rhughes@241
   243
};
rhughes@241
   244
rhughes@241
   245
static void
rhughes@241
   246
qsort_swap(void *p1, void *p2, size_t size)
rhughes@241
   247
{
rhughes@241
   248
	char buffer[size];
rhughes@241
   249
rhughes@241
   250
	memcpy(buffer, p1, size);
rhughes@241
   251
	memcpy(p1, p2, size);
rhughes@241
   252
	memcpy(p2, buffer, size);
rhughes@241
   253
}
rhughes@241
   254
rhughes@241
   255
static void
rhughes@241
   256
__qsort_with_data(void *base, size_t nelem, uint32_t *map,
rhughes@241
   257
		  struct qsort_context *ctx)
rhughes@241
   258
{
rhughes@241
   259
	void *p, *start, *end, *pivot;
rhughes@241
   260
	uint32_t *mp, *mstart, *mend, tmp;
rhughes@241
   261
	int left, right, result;
rhughes@241
   262
	size_t size = ctx->size;
rhughes@241
   263
rhughes@241
   264
	p = base;
rhughes@241
   265
	start = base;
rhughes@241
   266
	end = base + nelem * size;
rhughes@241
   267
	mp = map;
rhughes@241
   268
	mstart = map;
rhughes@241
   269
	mend = map + nelem;
ali@337
   270
	pivot = base + (rand() % nelem) * size;
rhughes@241
   271
rhughes@241
   272
	while (p < end) {
rhughes@241
   273
		result = ctx->compare(p, pivot, ctx->data);
rhughes@241
   274
		if (result < 0) {
rhughes@241
   275
			qsort_swap(p, start, size);
rhughes@241
   276
			tmp = *mp;
rhughes@241
   277
			*mp = *mstart;
rhughes@241
   278
			*mstart = tmp;
rhughes@241
   279
			if (start == pivot)
rhughes@241
   280
				pivot = p;
rhughes@241
   281
			start += size;
rhughes@241
   282
			mstart++;
rhughes@241
   283
			p += size;
rhughes@241
   284
			mp++;
rhughes@241
   285
		} else if (result == 0) {
rhughes@241
   286
			p += size;
rhughes@241
   287
			mp++;
rhughes@241
   288
		} else {
rhughes@241
   289
 			end -= size;
rhughes@241
   290
			mend--;
rhughes@241
   291
			qsort_swap(p, end, size);
rhughes@241
   292
			tmp = *mp;
rhughes@241
   293
			*mp = *mend;
rhughes@241
   294
			*mend = tmp;
rhughes@241
   295
			if (end == pivot)
rhughes@241
   296
				pivot = p;
rhughes@241
   297
		}
rhughes@241
   298
	}
rhughes@241
   299
rhughes@241
   300
	left = (start - base) / size;
rhughes@241
   301
	right = (base + nelem * size - end) / size;
rhughes@241
   302
	if (left > 1)
rhughes@241
   303
		__qsort_with_data(base, left, map, ctx);
rhughes@241
   304
	if (right > 1)
rhughes@241
   305
		__qsort_with_data(end, right, mend, ctx);
rhughes@241
   306
}
rhughes@241
   307
rhughes@241
   308
uint32_t *
rhughes@241
   309
razor_qsort_with_data(void *base, size_t nelem, size_t size,
rhughes@241
   310
		      razor_compare_with_data_func_t compare, void *data)
rhughes@241
   311
{
rhughes@241
   312
	struct qsort_context ctx;
rhughes@241
   313
	uint32_t *map;
rhughes@241
   314
	int i;
rhughes@241
   315
rhughes@241
   316
	if (nelem == 0)
rhughes@241
   317
		return NULL;
rhughes@241
   318
rhughes@241
   319
	ctx.size = size;
rhughes@241
   320
	ctx.compare = compare;
rhughes@241
   321
	ctx.data = data;
rhughes@241
   322
rhughes@241
   323
	map = malloc(nelem * sizeof (uint32_t));
rhughes@241
   324
	for (i = 0; i < nelem; i++)
rhughes@241
   325
		map[i] = i;
rhughes@241
   326
rhughes@241
   327
	__qsort_with_data(base, nelem, map, &ctx);
rhughes@241
   328
rhughes@241
   329
	return map;
rhughes@241
   330
}
ali@372
   331
ali@372
   332
void environment_init(struct environment *env)
ali@372
   333
{
ali@372
   334
	env->is_set = 0;
ali@372
   335
	array_init(&env->string_pool);
ali@372
   336
	array_init(&env->vars);
ali@372
   337
}
ali@372
   338
ali@372
   339
void environment_add_variable(struct environment *env,
ali@372
   340
			      const char *variable, const char *value)
ali@372
   341
{
ali@372
   342
	char *s;
ali@372
   343
	uint32_t *r;
ali@372
   344
	assert(!env->is_set);
ali@372
   345
ali@372
   346
	s = array_add(&env->string_pool,
ali@372
   347
		      strlen(variable) + strlen(value) + 2);
ali@372
   348
	sprintf(s, "%s=%s", variable, value);
ali@372
   349
	r = array_add(&env->vars, sizeof *r);
ali@372
   350
	*r = s - (char *)env->string_pool.data;
ali@372
   351
}
ali@372
   352
ali@372
   353
void environment_set(struct environment *env)
ali@372
   354
{
ali@372
   355
	int i, count;
ali@372
   356
	char *s;
ali@372
   357
        uint32_t *r;
ali@372
   358
ali@372
   359
	if (!env->is_set) {
ali@372
   360
		count = env->vars.size / sizeof(uint32_t);
ali@372
   361
		r = (uint32_t *)env->vars.data;
ali@372
   362
		for (i = 0; i < count; i++) {
ali@372
   363
			s = env->string_pool.data + *r++;
ali@372
   364
			putenv(s);
ali@372
   365
		}
ali@372
   366
ali@372
   367
		env->is_set = 1;
ali@372
   368
	}
ali@372
   369
}
ali@372
   370
ali@372
   371
void environment_unset(struct environment *env)
ali@372
   372
{
ali@372
   373
	int i, count;
ali@372
   374
	char c, *s, *t;
ali@372
   375
        uint32_t *r;
ali@372
   376
ali@372
   377
	if (env->is_set) {
ali@372
   378
		count = env->vars.size / sizeof(uint32_t);
ali@372
   379
		r = (uint32_t *)env->vars.data;
ali@372
   380
		for (i = 0; i < count; i++) {
ali@372
   381
			s = env->string_pool.data + *r++;
ali@372
   382
			t = strchr(s, '=') + 1;
ali@372
   383
			c = *t;
ali@372
   384
			*t = '\0';
ali@372
   385
			putenv(s);
ali@372
   386
			*t = c;
ali@372
   387
		}
ali@372
   388
ali@372
   389
		env->is_set = 0;
ali@372
   390
	}
ali@372
   391
}
ali@372
   392
ali@372
   393
void environment_release(struct environment *env)
ali@372
   394
{
ali@372
   395
	environment_unset(env);
ali@372
   396
	array_release(&env->string_pool);
ali@372
   397
	array_release(&env->vars);
ali@372
   398
}