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