Emulate atomic transactions
authorJ. Ali Harlow <ali@juiblex.co.uk>
Thu Feb 09 20:42:08 2012 +0000 (2012-02-09)
changeset 416d0aa9e0a6d04
parent 415 fbd3a02dcfde
child 417 c7063ba682e0
Emulate atomic transactions
configure.ac
librazor/Makefile.am
librazor/atomic-actions.c
librazor/atomic-emulate.c
librazor/atomic-ktm.c
librazor/atomic-none.c
librazor/atomic.c
librazor/razor-internal.h
librazor/razor.h
librazor/util.c
     1.1 --- a/configure.ac	Thu Feb 09 20:15:00 2012 +0000
     1.2 +++ b/configure.ac	Thu Feb 09 20:42:08 2012 +0000
     1.3 @@ -33,6 +33,15 @@
     1.4  AC_SYS_LARGEFILE
     1.5  AM_PROG_CC_C_O
     1.6  
     1.7 +AC_ARG_ENABLE([atomic],
     1.8 +	      [AS_HELP_STRING([--disable-atomic],
     1.9 +	      		      [disable atomic transactions])],
    1.10 +	      [],
    1.11 +	      [enable_atomic=yes])
    1.12 +if test "$enable_atomic" = "yes"; then
    1.13 +    AC_DEFINE([ENABLE_ATOMIC],[1],[Define if atomic transactions are wanted.])
    1.14 +fi
    1.15 +
    1.16  AC_MSG_CHECKING([for Microsoft Windows native API])
    1.17  case $host_os in
    1.18      *mingw*)	AC_DEFINE([MSWIN_API], 1,
    1.19 @@ -45,7 +54,7 @@
    1.20  AM_CONDITIONAL(MSWIN_API, test "$mswin_api" = "yes")
    1.21  AC_SUBST(EXTRA_LIBS)
    1.22  
    1.23 -if test "$mswin_api" = "yes"; then
    1.24 +if test "$enable_atomic" = "yes" -a "$mswin_api" = "yes"; then
    1.25      AC_MSG_CHECKING([for Microsoft Windows Kernel Transaction Manager])
    1.26      save_LIBS="$LIBS"
    1.27      LIBS="-lktmw32 $LIBS"
     2.1 --- a/librazor/Makefile.am	Thu Feb 09 20:15:00 2012 +0000
     2.2 +++ b/librazor/Makefile.am	Thu Feb 09 20:42:08 2012 +0000
     2.3 @@ -34,6 +34,10 @@
     2.4  	importer.c					\
     2.5  	merger.c					\
     2.6  	atomic.c					\
     2.7 +	atomic-ktm.c					\
     2.8 +	atomic-none.c					\
     2.9 +	atomic-emulate.c				\
    2.10 +	atomic-actions.c				\
    2.11  	transaction.c
    2.12  
    2.13  if HAVE_LUA
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/librazor/atomic-actions.c	Thu Feb 09 20:42:08 2012 +0000
     3.3 @@ -0,0 +1,499 @@
     3.4 +/*
     3.5 + * Copyright (C) 2012  J. Ali Harlow <ali@juiblex.co.uk>
     3.6 + *
     3.7 + * This program is free software; you can redistribute it and/or modify
     3.8 + * it under the terms of the GNU General Public License as published by
     3.9 + * the Free Software Foundation; either version 2 of the License, or
    3.10 + * (at your option) any later version.
    3.11 + *
    3.12 + * This program is distributed in the hope that it will be useful,
    3.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    3.15 + * GNU General Public License for more details.
    3.16 + *
    3.17 + * You should have received a copy of the GNU General Public License along
    3.18 + * with this program; if not, write to the Free Software Foundation, Inc.,
    3.19 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    3.20 + */
    3.21 +
    3.22 +#include "config.h"
    3.23 +
    3.24 +#if ENABLE_ATOMIC && !HAVE_WINDOWS_KTM
    3.25 +
    3.26 +#include <stdlib.h>
    3.27 +#include <stdio.h>
    3.28 +#include <string.h>
    3.29 +#include <limits.h>
    3.30 +#include <errno.h>
    3.31 +#include <sys/stat.h>
    3.32 +#include <sys/types.h>
    3.33 +#include <dirent.h>
    3.34 +#include <assert.h>
    3.35 +#include "razor-internal.h"
    3.36 +
    3.37 +char *atomic_action_attic_tmpnam(struct razor_atomic *atomic)
    3.38 +{
    3.39 +	char filename[17];
    3.40 +	sprintf(filename,"%03X",atomic->next_file_tag++);
    3.41 +	return razor_concat(atomic->toplevel, "/", filename, NULL);
    3.42 +}
    3.43 +
    3.44 +static struct atomic_action *
    3.45 +atomic_action_list_pop_head(struct atomic_action **list)
    3.46 +{
    3.47 +	struct atomic_action *head;
    3.48 +
    3.49 +	head = *list;
    3.50 +	if (head) {
    3.51 +		*list = head->next;
    3.52 +		head->next = NULL;
    3.53 +	}
    3.54 +
    3.55 +	return head;
    3.56 +}
    3.57 +
    3.58 +struct atomic_action *
    3.59 +atomic_action_list_prepend(struct atomic_action *list,
    3.60 +			   struct atomic_action *action)
    3.61 +{
    3.62 +	assert(action->next == NULL);
    3.63 +
    3.64 +	action->next=list;
    3.65 +
    3.66 +	return action;
    3.67 +}
    3.68 +
    3.69 +static struct atomic_action *
    3.70 +atomic_action_list_concat(struct atomic_action *list1,
    3.71 +			  struct atomic_action *list2)
    3.72 +{
    3.73 +	struct atomic_action *action;
    3.74 +
    3.75 +	if (!list1)
    3.76 +		return list2;
    3.77 +
    3.78 +	for(action=list1;action->next;action=action->next)
    3.79 +		;
    3.80 +
    3.81 +	action->next=list2;
    3.82 +
    3.83 +	return list1;
    3.84 +}
    3.85 +
    3.86 +void atomic_action_free(struct atomic_action *action)
    3.87 +{
    3.88 +	struct atomic_action *a;
    3.89 +
    3.90 +	while(action) {
    3.91 +		a = atomic_action_list_pop_head(&action);
    3.92 +
    3.93 +		free(a->args.path);
    3.94 +
    3.95 +		switch(a->type) {
    3.96 +		case ACTION_MAKE_DIRS:
    3.97 +			free(a->args.u.make_dirs.root);
    3.98 +			break;
    3.99 +		case ACTION_MOVE:
   3.100 +			free(a->args.u.move.dest);
   3.101 +			break;
   3.102 +#if HAVE_SYMLINK
   3.103 +		case ACTION_CREATE_SYMLINK:
   3.104 +			free(a->args.u.create_symlink.target);
   3.105 +			break;
   3.106 +#endif
   3.107 +		case ACTION_REMOVE:
   3.108 +		case ACTION_CREATE_DIR:
   3.109 +			break;
   3.110 +		}
   3.111 +
   3.112 +		free(a);
   3.113 +	}
   3.114 +}
   3.115 +
   3.116 +struct atomic_action *atomic_action_new(enum atomic_action_type type)
   3.117 +{
   3.118 +	struct atomic_action *action;
   3.119 +
   3.120 +	action = zalloc(sizeof *action);
   3.121 +	action->type = type;
   3.122 +
   3.123 +	return action;
   3.124 +}
   3.125 +
   3.126 +struct atomic_action *atomic_action_list_reverse(struct atomic_action *list)
   3.127 +{
   3.128 +	struct atomic_action *prev = NULL, *next;
   3.129 +
   3.130 +	while(list) {
   3.131 +		next = list->next;
   3.132 +		list->next = prev;
   3.133 +		prev = list;
   3.134 +		list = next;
   3.135 +	}
   3.136 +
   3.137 +	return prev;
   3.138 +}
   3.139 +
   3.140 +/*
   3.141 + * All action_ functions take 1 action and return a list of primitive
   3.142 + * actions they took (such that the first action in the list is the
   3.143 + * last action they took). Primitive actions are always reversable.
   3.144 + *
   3.145 + * On failure, an error should be set on the atomic object (if it is
   3.146 + * not already set), the action freed and NULL returned.
   3.147 + *
   3.148 + * Whether they succeed or fail, they take ownership of the passed
   3.149 + * action and should thus either free it or return it back to their
   3.150 + * caller as appropriate.
   3.151 + *
   3.152 + * A NULL return means that nothing was done which may, or may not,
   3.153 + * indicate an error.
   3.154 + */
   3.155 +
   3.156 +static struct atomic_action *
   3.157 +atomic_action_make_dirs(struct razor_atomic *atomic,
   3.158 +			struct atomic_action *action)
   3.159 +{
   3.160 +	char buffer[PATH_MAX], *p;
   3.161 +	const char *slash, *next;
   3.162 +	struct atomic_action *primitives = NULL;
   3.163 +	struct atomic_action *prim;
   3.164 +
   3.165 +	if (razor_atomic_in_error_state(atomic)) {
   3.166 +		atomic_action_free(action);
   3.167 +		return NULL;
   3.168 +	}
   3.169 +
   3.170 +	strcpy(buffer, action->args.u.make_dirs.root);
   3.171 +	p = buffer + strlen(buffer);
   3.172 +	slash = action->args.path;
   3.173 +	for (; *slash != '\0'; slash = next) {
   3.174 +#ifdef MSWIN_API
   3.175 +		next = strpbrk(slash + 1, "/\\");
   3.176 +#else
   3.177 +		next = strchr(slash + 1, '/');
   3.178 +#endif
   3.179 +		if (next == NULL)
   3.180 +			break;
   3.181 +
   3.182 +		memcpy(p, slash, next - slash);
   3.183 +		p += next - slash;
   3.184 +		*p = '\0';
   3.185 +
   3.186 +		if (razor_valid_root_name(buffer))
   3.187 +			continue;
   3.188 +
   3.189 +		prim = atomic_action_new(ACTION_CREATE_DIR);
   3.190 +		prim->args.path = strdup(buffer);
   3.191 +		prim->args.u.create_dir.mode = S_IRWXU | S_IRWXG | S_IRWXO;
   3.192 +		primitives = atomic_action_list_prepend(primitives, prim);
   3.193 +	}
   3.194 +	primitives = atomic_action_list_reverse(primitives);
   3.195 +
   3.196 +	return atomic_action_do(atomic, primitives);
   3.197 +}
   3.198 +
   3.199 +static struct atomic_action *
   3.200 +atomic_action_remove(struct razor_atomic *atomic, struct atomic_action *action)
   3.201 +{
   3.202 +#ifdef MSWIN_API
   3.203 +	wchar_t *path;
   3.204 +	_WDIR *dir;
   3.205 +#else
   3.206 +	DIR *dir;
   3.207 +#endif
   3.208 +	struct atomic_action *prim;
   3.209 +
   3.210 +	if (razor_atomic_in_error_state(atomic)) {
   3.211 +		atomic_action_free(action);
   3.212 +		return NULL;
   3.213 +	}
   3.214 +
   3.215 +	/*
   3.216 +	 * Non-empty directories should NOT be removed
   3.217 +	 */
   3.218 +#ifdef MSWIN_API
   3.219 +	path = razor_utf8_to_utf16(action->args.path, -1);
   3.220 +
   3.221 +	dir = _wopendir(path);
   3.222 +	if (dir && _wreaddir(dir)) {
   3.223 +		atomic_action_free(action);
   3.224 +		action = NULL;
   3.225 +	}
   3.226 +	_wclosedir(dir);
   3.227 +#else
   3.228 +	dir = opendir(action->args.path);
   3.229 +	if (dir && readdir(dir)) {
   3.230 +		atomic_action_free(action);
   3.231 +		action = NULL;
   3.232 +	}
   3.233 +	closedir(dir);
   3.234 +#endif
   3.235 +
   3.236 +	if (action) {
   3.237 +		prim = atomic_action_new(ACTION_MOVE);
   3.238 +		prim->args.path = strdup(action->args.path);
   3.239 +		prim->args.u.move.dest = atomic_action_attic_tmpnam(atomic);
   3.240 +
   3.241 +		atomic_action_free(action);
   3.242 +	} else
   3.243 +		prim = NULL;
   3.244 +
   3.245 +	return prim ? atomic_action_do(atomic, prim) : NULL;
   3.246 +}
   3.247 +
   3.248 +static struct atomic_action *
   3.249 +atomic_action_create_dir(struct razor_atomic *atomic,
   3.250 +			 struct atomic_action *action)
   3.251 +{
   3.252 +	mode_t mode;
   3.253 +
   3.254 +	if (razor_atomic_in_error_state(atomic)) {
   3.255 +		atomic_action_free(action);
   3.256 +		return NULL;
   3.257 +	}
   3.258 +
   3.259 +	mode = action->args.u.create_dir.mode & (S_IRWXU | S_IRWXG | S_IRWXO);
   3.260 +
   3.261 +	if (!mkdir(action->args.path, mode))
   3.262 +		return 0;
   3.263 +
   3.264 +	if (errno != EEXIST || chmod(action->args.path, mode) < 0) {
   3.265 +		if (!atomic->error_str)
   3.266 +			razor_atomic_set_error_str(atomic, action->args.path,
   3.267 +						   strerror(errno));
   3.268 +		atomic_action_free(action);
   3.269 +		return NULL;
   3.270 +	}
   3.271 +
   3.272 +	return action;
   3.273 +}
   3.274 +
   3.275 +static struct atomic_action *atomic_action_rmdir(struct razor_atomic *atomic,
   3.276 +						 struct atomic_action *action)
   3.277 +{
   3.278 +	if (razor_atomic_in_error_state(atomic)) {
   3.279 +		atomic_action_free(action);
   3.280 +		return NULL;
   3.281 +	}
   3.282 +
   3.283 +	if (rmdir(action->args.path) < 0) {
   3.284 +		if (!atomic->error_str)
   3.285 +			razor_atomic_set_error_str(atomic, action->args.path,
   3.286 +						   strerror(errno));
   3.287 +		atomic_action_free(action);
   3.288 +		return NULL;
   3.289 +	} else
   3.290 +		return action;
   3.291 +}
   3.292 +
   3.293 +#if HAVE_SYMLINK
   3.294 +static struct atomic_action *
   3.295 +atomic_action_create_symlink(struct razor_atomic *atomic,
   3.296 +			     struct atomic_action *action)
   3.297 +{
   3.298 +	int r;
   3.299 +
   3.300 +	if (razor_atomic_in_error_state(atomic)) {
   3.301 +		atomic_action_free(action);
   3.302 +		return NULL;
   3.303 +	}
   3.304 +
   3.305 +	r = symlink(action->args.u.create_symlink.target, action->args.path);
   3.306 +	if (r < 0) {
   3.307 +		if (!atomic->error_str)
   3.308 +			razor_atomic_set_error_str(atomic, NULL,
   3.309 +						   strerror(errno));
   3.310 +		atomic_action_free(action);
   3.311 +		return NULL;
   3.312 +	}
   3.313 +
   3.314 +	return action;
   3.315 +}
   3.316 +
   3.317 +static struct atomic_action *
   3.318 +atomic_action_remove_symlink(struct razor_atomic *atomic,
   3.319 +			     struct atomic_action *action)
   3.320 +{
   3.321 +	if (razor_atomic_in_error_state(atomic)) {
   3.322 +		atomic_action_free(action);
   3.323 +		return NULL;
   3.324 +	}
   3.325 +
   3.326 +	if (unlink(action->args.path) < 0) {
   3.327 +		if (!atomic->error_str)
   3.328 +			razor_atomic_set_error_str(atomic, NULL,
   3.329 +						   strerror(errno));
   3.330 +		atomic_action_free(action);
   3.331 +		return NULL;
   3.332 +	}
   3.333 +
   3.334 +	return action;
   3.335 +}
   3.336 +#endif
   3.337 +
   3.338 +static int
   3.339 +move_file(struct razor_atomic *atomic, const char *path, const char *dest)
   3.340 +{
   3.341 +#ifdef MSWIN_API
   3.342 +	wchar_t *oldbuf, *newbuf;
   3.343 +	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
   3.344 +
   3.345 +	newbuf = razor_utf8_to_utf16(dest, -1);
   3.346 +	oldbuf = razor_utf8_to_utf16(path, -1);
   3.347 +
   3.348 +	/*
   3.349 +	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
   3.350 +	 * cover every case we care about _except_ replacing an empty
   3.351 +	 * directory with a file. Calling RemoveDirectory() will deal
   3.352 +	 * with this case while having no effect in all other cases.
   3.353 +	 */
   3.354 +	(void)RemoveDirectoryW(newbuf);
   3.355 +
   3.356 +	if (!MoveFileExW(oldbuf, newbuf, flags)) {
   3.357 +		if (!atomic->error_str)
   3.358 +			razor_atomic_set_error_mswin(atomic, newbuf,
   3.359 +						     GetLastError());
   3.360 +		return -1;
   3.361 +	}
   3.362 +
   3.363 +	free(newbuf);
   3.364 +	free(oldbuf);
   3.365 +#else
   3.366 +	if (rename(path, dest)) {
   3.367 +		if (!atomic->error_str)
   3.368 +			razor_atomic_set_error_str(atomic, dest,
   3.369 +						   strerror(errno));
   3.370 +		return -1;
   3.371 +	}
   3.372 +#endif
   3.373 +
   3.374 +	return 0;
   3.375 +}
   3.376 +
   3.377 +static struct atomic_action *
   3.378 +atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action)
   3.379 +{
   3.380 +	if (razor_atomic_in_error_state(atomic)) {
   3.381 +		atomic_action_free(action);
   3.382 +		return NULL;
   3.383 +	}
   3.384 +
   3.385 +	if (move_file(atomic, action->args.path, action->args.u.move.dest)) {
   3.386 +		atomic_action_free(action);
   3.387 +		return NULL;
   3.388 +	}
   3.389 +
   3.390 +	return action;
   3.391 +}
   3.392 +
   3.393 +static struct atomic_action *
   3.394 +atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action)
   3.395 +{
   3.396 +	if (razor_atomic_in_error_state(atomic)) {
   3.397 +		atomic_action_free(action);
   3.398 +		return NULL;
   3.399 +	}
   3.400 +
   3.401 +	if (move_file(atomic, action->args.u.move.dest, action->args.path)) {
   3.402 +		atomic_action_free(action);
   3.403 +		return NULL;
   3.404 +	}
   3.405 +
   3.406 +	return action;
   3.407 +}
   3.408 +
   3.409 +static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic,
   3.410 +						 struct atomic_action *action)
   3.411 +{
   3.412 +	switch(action->type) {
   3.413 +	case ACTION_MAKE_DIRS:
   3.414 +		action = atomic_action_make_dirs(atomic, action);
   3.415 +		break;
   3.416 +	case ACTION_REMOVE:
   3.417 +		action = atomic_action_remove(atomic, action);
   3.418 +		break;
   3.419 +	case ACTION_CREATE_DIR:
   3.420 +		action = atomic_action_create_dir(atomic, action);
   3.421 +		break;
   3.422 +	case ACTION_MOVE:
   3.423 +		action = atomic_action_move(atomic, action);
   3.424 +		break;
   3.425 +#if HAVE_SYMLINK
   3.426 +	case ACTION_CREATE_SYMLINK:
   3.427 +		action = atomic_action_create_symlink(atomic, action);
   3.428 +		break;
   3.429 +#endif
   3.430 +	}
   3.431 +	return action;
   3.432 +}
   3.433 +
   3.434 +static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic,
   3.435 +						   struct atomic_action *action)
   3.436 +{
   3.437 +	switch(action->type) {
   3.438 +	case ACTION_MAKE_DIRS:
   3.439 +	case ACTION_REMOVE:
   3.440 +		/* Complex actions: should never happen */
   3.441 +		break;
   3.442 +	case ACTION_CREATE_DIR:
   3.443 +		action = atomic_action_rmdir(atomic, action);
   3.444 +		break;
   3.445 +	case ACTION_MOVE:
   3.446 +		action = atomic_action_unmove(atomic, action);
   3.447 +		break;
   3.448 +#if HAVE_SYMLINK
   3.449 +	case ACTION_CREATE_SYMLINK:
   3.450 +		action = atomic_action_remove_symlink(atomic, action);
   3.451 +		break;
   3.452 +#endif
   3.453 +	}
   3.454 +	return action;
   3.455 +}
   3.456 +
   3.457 +/*
   3.458 + * Note that undo has no error checking.
   3.459 + */
   3.460 +
   3.461 +void atomic_action_undo(struct razor_atomic *atomic,
   3.462 +			struct atomic_action *actions)
   3.463 +{
   3.464 +	struct atomic_action *a;
   3.465 +
   3.466 +	atomic->in_undo = 1;
   3.467 +
   3.468 +	while (actions) {
   3.469 +		a = atomic_action_list_pop_head(&actions);
   3.470 +		a = atomic_action_reverse(atomic, a);
   3.471 +		atomic_action_free(a);
   3.472 +	}
   3.473 +
   3.474 +	atomic->in_undo = 0;
   3.475 +}
   3.476 +
   3.477 +struct atomic_action *atomic_action_do(struct razor_atomic *atomic,
   3.478 +				       struct atomic_action *actions)
   3.479 +{
   3.480 +	struct atomic_action *done = NULL, *a;
   3.481 +
   3.482 +	if (razor_atomic_in_error_state(atomic)) {
   3.483 +		atomic_action_free(actions);
   3.484 +		return NULL;
   3.485 +	}
   3.486 +
   3.487 +	while (actions) {
   3.488 +		a = atomic_action_list_pop_head(&actions);
   3.489 +		a = atomic_action_apply(atomic, a);
   3.490 +		if (a)
   3.491 +			done = atomic_action_list_concat(a, done);
   3.492 +		if (razor_atomic_in_error_state(atomic)) {
   3.493 +			atomic_action_undo(atomic, done);
   3.494 +			done = NULL;
   3.495 +			atomic_action_free(actions);
   3.496 +		}
   3.497 +	}
   3.498 +
   3.499 +	return done;
   3.500 +}
   3.501 +
   3.502 +#endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/librazor/atomic-emulate.c	Thu Feb 09 20:42:08 2012 +0000
     4.3 @@ -0,0 +1,249 @@
     4.4 +/*
     4.5 + * Copyright (C) 2012  J. Ali Harlow <ali@juiblex.co.uk>
     4.6 + *
     4.7 + * This program is free software; you can redistribute it and/or modify
     4.8 + * it under the terms of the GNU General Public License as published by
     4.9 + * the Free Software Foundation; either version 2 of the License, or
    4.10 + * (at your option) any later version.
    4.11 + *
    4.12 + * This program is distributed in the hope that it will be useful,
    4.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    4.15 + * GNU General Public License for more details.
    4.16 + *
    4.17 + * You should have received a copy of the GNU General Public License along
    4.18 + * with this program; if not, write to the Free Software Foundation, Inc.,
    4.19 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    4.20 + */
    4.21 +
    4.22 +#include "config.h"
    4.23 +
    4.24 +#if ENABLE_ATOMIC && !HAVE_WINDOWS_KTM
    4.25 +
    4.26 +#include <stdlib.h>
    4.27 +#include <string.h>
    4.28 +#include <unistd.h>
    4.29 +#include <sys/types.h>
    4.30 +#include <sys/stat.h>
    4.31 +#include <fcntl.h>
    4.32 +#include <dirent.h>
    4.33 +#include <errno.h>
    4.34 +#include "razor-internal.h"
    4.35 +
    4.36 +/*
    4.37 + * Emulated atomic support
    4.38 + *
    4.39 + * This implementation is better than nothing, but is certainly not atomic.
    4.40 + * It does have a couple of advantages over atomic-none:
    4.41 + *	- If a file operation fails while a package is being installed we
    4.42 + *	  have a good chance of being able to rollback the transaction to
    4.43 + *	  a well-known state.
    4.44 + *	- We behave similarly to atomic-ktm in that changes are not visible
    4.45 + *	  on disk to non-atomic operations (eg., scripts) until the atomic
    4.46 + *	  is committed. This makes the testsuite more likely to pick up
    4.47 + *	  problems that would otherwise only be found when using razor on
    4.48 + *	  an MS-Windows system which supports KTM.
    4.49 + */
    4.50 +
    4.51 +#ifndef O_BINARY
    4.52 +#define O_BINARY	0
    4.53 +#endif
    4.54 +
    4.55 +static void recursive_remove(const char *directory)
    4.56 +{
    4.57 +	DIR *dp;
    4.58 +	struct dirent *dirp;
    4.59 +	char *buf;
    4.60 +
    4.61 +	dp = opendir(directory);
    4.62 +	while((dirp = readdir(dp))) {
    4.63 +		if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) {
    4.64 +			buf = malloc(strlen(directory) + strlen(dirp->d_name)
    4.65 +				     + 2);
    4.66 +			sprintf(buf, "%s/%s", directory, dirp->d_name);
    4.67 +			if (remove(buf) < 0)
    4.68 +				recursive_remove(buf);
    4.69 +			free(buf);
    4.70 +		}
    4.71 +	}
    4.72 +
    4.73 +	rmdir(directory);
    4.74 +}
    4.75 +
    4.76 +RAZOR_EXPORT struct razor_atomic *razor_atomic_open(const char *description)
    4.77 +{
    4.78 +	struct razor_atomic *atomic;
    4.79 +
    4.80 +	atomic = zalloc(sizeof *atomic);
    4.81 +
    4.82 +	atomic->toplevel = strdup(".atomic-XXXXXX");
    4.83 +	if (!mkdtemp(atomic->toplevel)) {
    4.84 +		free(atomic->toplevel);
    4.85 +		free(atomic);
    4.86 +		return NULL;
    4.87 +	}
    4.88 +
    4.89 +	atomic->description = strdup(description);
    4.90 +
    4.91 +	return atomic;
    4.92 +}
    4.93 +
    4.94 +RAZOR_EXPORT int razor_atomic_commit(struct razor_atomic *atomic)
    4.95 +{
    4.96 +	struct atomic_action *actions;
    4.97 +
    4.98 +	if (razor_atomic_in_error_state(atomic))
    4.99 +		return -1;
   4.100 +
   4.101 +	if (atomic->actions) {
   4.102 +		actions = atomic_action_list_reverse(atomic->actions);
   4.103 +		atomic->actions = NULL;
   4.104 +		actions = atomic_action_do(atomic, actions);
   4.105 +		atomic_action_free(actions);
   4.106 +	}
   4.107 +
   4.108 +	if (atomic->toplevel) {
   4.109 +		recursive_remove(atomic->toplevel);
   4.110 +		free(atomic->toplevel);
   4.111 +		atomic->toplevel = NULL;
   4.112 +	}
   4.113 +
   4.114 +	return !!atomic->error_str;
   4.115 +}
   4.116 +
   4.117 +RAZOR_EXPORT void razor_atomic_destroy(struct razor_atomic *atomic)
   4.118 +{
   4.119 +	if (atomic->toplevel) {
   4.120 +		recursive_remove(atomic->toplevel);
   4.121 +		free(atomic->toplevel);
   4.122 +		atomic->toplevel = NULL;
   4.123 +	}
   4.124 +
   4.125 +	free(atomic->error_path);
   4.126 +	free(atomic->error_str);
   4.127 +	free(atomic->error_msg);
   4.128 +	free(atomic);
   4.129 +}
   4.130 +
   4.131 +RAZOR_EXPORT int
   4.132 +razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
   4.133 +		       const char *path)
   4.134 +{
   4.135 +	struct atomic_action *a;
   4.136 +
   4.137 +	if (razor_atomic_in_error_state(atomic))
   4.138 +		return -1;
   4.139 +
   4.140 +	a = atomic_action_new(ACTION_MAKE_DIRS);
   4.141 +	a->args.path = strdup(path);
   4.142 +	a->args.u.make_dirs.root = strdup(root);
   4.143 +	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   4.144 +
   4.145 +	return 0;
   4.146 +}
   4.147 +
   4.148 +RAZOR_EXPORT int
   4.149 +razor_atomic_remove(struct razor_atomic *atomic, const char *path)
   4.150 +{
   4.151 +	struct atomic_action *a;
   4.152 +
   4.153 +	if (razor_atomic_in_error_state(atomic))
   4.154 +		return -1;
   4.155 +
   4.156 +	a = atomic_action_new(ACTION_REMOVE);
   4.157 +	a->args.path = strdup(path);
   4.158 +	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   4.159 +
   4.160 +	return 0;
   4.161 +}
   4.162 +
   4.163 +RAZOR_EXPORT int
   4.164 +razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
   4.165 +			 const char *newpath)
   4.166 +{
   4.167 +	struct atomic_action *a;
   4.168 +
   4.169 +	if (razor_atomic_in_error_state(atomic))
   4.170 +		return -1;
   4.171 +
   4.172 +	a = atomic_action_new(ACTION_MOVE);
   4.173 +	a->args.path = strdup(oldpath);
   4.174 +	a->args.u.move.dest = strdup(newpath);
   4.175 +	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   4.176 +
   4.177 +	return 0;
   4.178 +}
   4.179 +
   4.180 +RAZOR_EXPORT int
   4.181 +razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
   4.182 +			mode_t mode)
   4.183 +{
   4.184 +	struct atomic_action *a;
   4.185 +
   4.186 +	if (razor_atomic_in_error_state(atomic))
   4.187 +		return -1;
   4.188 +
   4.189 +	a = atomic_action_new(ACTION_CREATE_DIR);
   4.190 +	a->args.path = strdup(dirname);
   4.191 +	a->args.u.create_dir.mode = mode;
   4.192 +	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   4.193 +
   4.194 +	return 0;
   4.195 +}
   4.196 +
   4.197 +RAZOR_EXPORT int
   4.198 +razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
   4.199 +			    const char *path)
   4.200 +{
   4.201 +#if HAVE_SYMLINK
   4.202 +	struct atomic_action *a;
   4.203 +#endif
   4.204 +
   4.205 +	if (razor_atomic_in_error_state(atomic))
   4.206 +		return -1;
   4.207 +
   4.208 +#if HAVE_SYMLINK
   4.209 +	a = atomic_action_new(ACTION_CREATE_SYMLINK);
   4.210 +	a->args.path = strdup(path);
   4.211 +	a->args.u.create_symlink.target = strdup(target);
   4.212 +	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   4.213 +
   4.214 +	return 0;
   4.215 +#else
   4.216 +	razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported "
   4.217 +						 "on this platform");
   4.218 +
   4.219 +	return -1;
   4.220 +#endif
   4.221 +}
   4.222 +
   4.223 +RAZOR_EXPORT int
   4.224 +razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
   4.225 +                         mode_t mode)
   4.226 +{
   4.227 +	int fd;
   4.228 +	struct atomic_action *a;
   4.229 +	char *tmpnam;
   4.230 +
   4.231 +	if (razor_atomic_in_error_state(atomic))
   4.232 +		return -1;
   4.233 +
   4.234 +	atomic->error_path = strdup(filename);
   4.235 +	tmpnam = atomic_action_attic_tmpnam(atomic);
   4.236 +	fd = open(tmpnam, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
   4.237 +		  mode & (S_IRWXU | S_IRWXG | S_IRWXO));
   4.238 +
   4.239 +	if (fd == -1)
   4.240 +		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
   4.241 +	else {
   4.242 +		a = atomic_action_new(ACTION_MOVE);
   4.243 +		a->args.path = tmpnam;
   4.244 +		a->args.u.move.dest = strdup(filename);
   4.245 +		atomic->actions = atomic_action_list_prepend(atomic->actions,
   4.246 +							     a);
   4.247 +	}
   4.248 +
   4.249 +	return fd;
   4.250 +}
   4.251 +
   4.252 +#endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/librazor/atomic-ktm.c	Thu Feb 09 20:42:08 2012 +0000
     5.3 @@ -0,0 +1,574 @@
     5.4 +/*
     5.5 + * Copyright (C) 2011-2012  J. Ali Harlow <ali@juiblex.co.uk>
     5.6 + *
     5.7 + * This program is free software; you can redistribute it and/or modify
     5.8 + * it under the terms of the GNU General Public License as published by
     5.9 + * the Free Software Foundation; either version 2 of the License, or
    5.10 + * (at your option) any later version.
    5.11 + *
    5.12 + * This program is distributed in the hope that it will be useful,
    5.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.15 + * GNU General Public License for more details.
    5.16 + *
    5.17 + * You should have received a copy of the GNU General Public License along
    5.18 + * with this program; if not, write to the Free Software Foundation, Inc.,
    5.19 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    5.20 + */
    5.21 +
    5.22 +#include "config.h"
    5.23 +
    5.24 +#if HAVE_WINDOWS_KTM
    5.25 +
    5.26 +#include <stdlib.h>
    5.27 +#include <windows.h>
    5.28 +#include <stdio.h>
    5.29 +#include <limits.h>
    5.30 +#include <errno.h>
    5.31 +#include <unistd.h>
    5.32 +#include <fcntl.h>
    5.33 +#include <sys/stat.h>
    5.34 +#include <string.h>
    5.35 +#include <assert.h>
    5.36 +#include <wchar.h>
    5.37 +#include <ktmw32.h>
    5.38 +
    5.39 +#include "razor.h"
    5.40 +#include "razor-internal.h"
    5.41 +
    5.42 +#define RAZOR_ASCII_ISALPHA(c)	\
    5.43 +			((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z')
    5.44 +
    5.45 +static int
    5.46 +razor_valid_root_name2(const wchar_t *name)
    5.47 +{
    5.48 +	if (razor_allow_all_root_names())
    5.49 +		return !wcschr(name, '/');
    5.50 +
    5.51 +	return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
    5.52 +	       name[2] == '\0';
    5.53 +}
    5.54 +
    5.55 +struct razor_wstr {
    5.56 +	wchar_t *str;
    5.57 +	int len, allocated;
    5.58 +};
    5.59 +
    5.60 +static struct razor_wstr *
    5.61 +razor_wstr_create(const char *init, int len)
    5.62 +{
    5.63 +	int n;
    5.64 +	struct razor_wstr *wstr;
    5.65 +
    5.66 +	wstr = malloc(sizeof(struct razor_wstr));
    5.67 +
    5.68 +	n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0);
    5.69 +	if (len >= 0 && init[len])
    5.70 +		wstr->len = n++;
    5.71 +	else
    5.72 +		wstr->len = n - 1;
    5.73 +
    5.74 +	wstr->allocated = n * 2;
    5.75 +	wstr->str = malloc(wstr->allocated * sizeof(wchar_t));
    5.76 +	if (!wstr->str) {
    5.77 +		free(wstr);
    5.78 +		return NULL;
    5.79 +	}
    5.80 +
    5.81 +	(void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n);
    5.82 +	if (len >= 0 && init[len])
    5.83 +		wstr->str[wstr->len] = 0;
    5.84 +
    5.85 +	return wstr;
    5.86 +}
    5.87 +
    5.88 +static int
    5.89 +razor_wstr_append(struct razor_wstr *wstr, const char *s, int len)
    5.90 +{
    5.91 +	int n, allocated;
    5.92 +	wchar_t *str;
    5.93 +
    5.94 +	n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0);
    5.95 +	if (len < 0 || !s[len])
    5.96 +		n--;
    5.97 +
    5.98 +	if (wstr->allocated <= wstr->len + n) {
    5.99 +		allocated = (wstr->len + n + 1) * 2;
   5.100 +		str = realloc(wstr->str, allocated * sizeof(wchar_t));
   5.101 +		if (!str)
   5.102 +			return -1;
   5.103 +		wstr->allocated = allocated;
   5.104 +		wstr->str = str;
   5.105 +	}
   5.106 +
   5.107 +	(void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n);
   5.108 +	wstr->len += n;
   5.109 +	wstr->str[wstr->len] = 0;
   5.110 +
   5.111 +	return 0;
   5.112 +}
   5.113 +
   5.114 +static void
   5.115 +razor_wstr_destroy(struct razor_wstr *wstr)
   5.116 +{
   5.117 +	free(wstr->str);
   5.118 +	free(wstr);
   5.119 +}
   5.120 +
   5.121 +RAZOR_EXPORT struct razor_atomic *
   5.122 +razor_atomic_open(const char *description)
   5.123 +{
   5.124 +	wchar_t *buf;
   5.125 +	struct razor_atomic *atomic;
   5.126 +
   5.127 +	atomic = zalloc(sizeof *atomic);
   5.128 +	buf = razor_utf8_to_utf16(description, -1);
   5.129 +	atomic->transaction = CreateTransaction(NULL, 0,
   5.130 +						TRANSACTION_DO_NOT_PROMOTE,
   5.131 +						0, 0, 0, buf);
   5.132 +	free(buf);
   5.133 +
   5.134 +	return atomic;
   5.135 +}
   5.136 +
   5.137 +void
   5.138 +razor_atomic_set_error_str(struct razor_atomic *atomic, const wchar_t *path,
   5.139 +			   const char *str)
   5.140 +{
   5.141 +	assert(!atomic->error_str);
   5.142 +
   5.143 +	free(atomic->error_path);
   5.144 +
   5.145 +	if (path)
   5.146 +		atomic->error_path = razor_utf16_to_utf8(path, -1);
   5.147 +	else
   5.148 +		atomic->error_path = NULL;
   5.149 +
   5.150 +	atomic->error_str = strdup(str);
   5.151 +}
   5.152 +
   5.153 +static void
   5.154 +razor_atomic_set_error(struct razor_atomic *atomic, const wchar_t *path,
   5.155 +		       DWORD error)
   5.156 +{
   5.157 +	wchar_t *buf;
   5.158 +
   5.159 +	assert(!atomic->error_str);
   5.160 +
   5.161 +	free(atomic->error_path);
   5.162 +
   5.163 +	if (path)
   5.164 +		atomic->error_path = razor_utf16_to_utf8(path, -1);
   5.165 +	else
   5.166 +		atomic->error_path = NULL;
   5.167 +
   5.168 +	FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|
   5.169 +		       FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
   5.170 +		       NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
   5.171 +		       (LPWSTR)&buf, 0, NULL);
   5.172 +	atomic->error_str = razor_utf16_to_utf8(buf, -1);
   5.173 +	LocalFree(buf);
   5.174 +}
   5.175 +
   5.176 +RAZOR_EXPORT int
   5.177 +razor_atomic_commit(struct razor_atomic *atomic)
   5.178 +{
   5.179 +	int retval;
   5.180 +
   5.181 +	if (razor_atomic_in_error_state(atomic))
   5.182 +		return -1;
   5.183 +
   5.184 +	retval = !CommitTransaction(atomic->transaction);
   5.185 +
   5.186 +	if (retval) {
   5.187 +		razor_atomic_set_error(atomic, NULL, GetLastError());
   5.188 +		RollbackTransaction(atomic->transaction);
   5.189 +	}
   5.190 +
   5.191 +	CloseHandle(atomic->transaction);
   5.192 +	atomic->transaction = INVALID_HANDLE_VALUE;
   5.193 +
   5.194 +	return retval;
   5.195 +}
   5.196 +
   5.197 +RAZOR_EXPORT void
   5.198 +razor_atomic_destroy(struct razor_atomic *atomic)
   5.199 +{
   5.200 +	int i;
   5.201 +
   5.202 +	for(i = 0; i < atomic->n_files; i++) {
   5.203 +		if (atomic->files[i].h != INVALID_HANDLE_VALUE) {
   5.204 +			CloseHandle(atomic->files[i].h);
   5.205 +			free(atomic->files[i].path);
   5.206 +		}
   5.207 +	}
   5.208 +	free(atomic->files);
   5.209 +	if (atomic->transaction != INVALID_HANDLE_VALUE) {
   5.210 +		RollbackTransaction(atomic->transaction);
   5.211 +		CloseHandle(atomic->transaction);
   5.212 +	}
   5.213 +	free(atomic->error_path);
   5.214 +	free(atomic->error_str);
   5.215 +	free(atomic->error_msg);
   5.216 +	free(atomic);
   5.217 +}
   5.218 +
   5.219 +RAZOR_EXPORT int
   5.220 +razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
   5.221 +		       const char *path)
   5.222 +{
   5.223 +	struct razor_wstr *buffer;
   5.224 +	const char *slash, *next;
   5.225 +	WIN32_FILE_ATTRIBUTE_DATA fa;
   5.226 +	DWORD err;
   5.227 +	int r, creating = 0;
   5.228 +
   5.229 +	if (razor_atomic_in_error_state(atomic))
   5.230 +		return -1;
   5.231 +
   5.232 +	buffer = razor_wstr_create(root, -1);
   5.233 +	slash = path;
   5.234 +
   5.235 +	for (; *slash != '\0'; slash = next) {
   5.236 +		next = strpbrk(slash + 1, "/\\");
   5.237 +		if (next == NULL)
   5.238 +			break;
   5.239 +
   5.240 +		razor_wstr_append(buffer, slash, next - slash);
   5.241 +
   5.242 +		if (!creating) {
   5.243 +			if (razor_valid_root_name2(buffer->str))
   5.244 +				continue;
   5.245 +
   5.246 +			r = GetFileAttributesTransactedW(buffer->str,
   5.247 +							 GetFileExInfoStandard,
   5.248 +							 &fa,
   5.249 +							 atomic->transaction);
   5.250 +
   5.251 +			if (!r) {
   5.252 +				err = GetLastError();
   5.253 +				if (err == ERROR_FILE_NOT_FOUND) {
   5.254 +					creating = 1;
   5.255 +				} else {
   5.256 +					razor_atomic_set_error(atomic,
   5.257 +							       buffer->str,
   5.258 +							       err);
   5.259 +					razor_wstr_destroy(buffer);
   5.260 +					return -1;
   5.261 +				}
   5.262 +			} else if (!(fa.dwFileAttributes&
   5.263 +				     FILE_ATTRIBUTE_DIRECTORY)) {
   5.264 +				razor_atomic_set_error_str(atomic, buffer->str,
   5.265 +							   "Not a directory");
   5.266 +				razor_wstr_destroy(buffer);
   5.267 +				return -1;
   5.268 +			}
   5.269 +		}
   5.270 +		if (creating) {
   5.271 +			if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL,
   5.272 +							atomic->transaction)) {
   5.273 +				razor_atomic_set_error(atomic, buffer->str,
   5.274 +						       GetLastError());
   5.275 +				razor_wstr_destroy(buffer);
   5.276 +				return -1;
   5.277 +			}
   5.278 +
   5.279 +			/* FIXME: What to do about permissions for dirs we
   5.280 +			 * have to create but are not in the cpio archive? */
   5.281 +		}
   5.282 +	}
   5.283 +
   5.284 +	razor_wstr_destroy(buffer);
   5.285 +
   5.286 +	return 0;
   5.287 +}
   5.288 +
   5.289 +RAZOR_EXPORT int
   5.290 +razor_atomic_remove(struct razor_atomic *atomic, const char *path)
   5.291 +{
   5.292 +	wchar_t *buf;
   5.293 +	DWORD err;
   5.294 +
   5.295 +	if (razor_atomic_in_error_state(atomic))
   5.296 +		return -1;
   5.297 +
   5.298 +	buf = razor_utf8_to_utf16(path, -1);
   5.299 +
   5.300 +	if (DeleteFileTransactedW(buf, atomic->transaction)) {
   5.301 +		free(buf);
   5.302 +		return 0;
   5.303 +	}
   5.304 +
   5.305 +	err = GetLastError();
   5.306 +	if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
   5.307 +		free(buf);
   5.308 +		return 0;
   5.309 +	}
   5.310 +
   5.311 +	if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL,
   5.312 +					 atomic->transaction)) {
   5.313 +		if (DeleteFileTransactedW(buf, atomic->transaction)) {
   5.314 +			free(buf);
   5.315 +			return 0;
   5.316 +		}
   5.317 +		err = GetLastError();
   5.318 +	}
   5.319 +
   5.320 +	if (RemoveDirectoryTransactedW(buf, atomic->transaction) ||
   5.321 +	    GetLastError() == ERROR_DIR_NOT_EMPTY) {
   5.322 +		free(buf);
   5.323 +		return 0;
   5.324 +	}
   5.325 +
   5.326 +	/*
   5.327 +	 * It would be tempting to use:
   5.328 +	 * 	MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)
   5.329 +	 * but unless we can guarantee that the system will be rebooted
   5.330 +	 * before we (or some other application) write another file with the
   5.331 +	 * same path, this is likely to cause more problems than it solves.
   5.332 +	 */
   5.333 +
   5.334 +	razor_atomic_set_error(atomic, buf, err);
   5.335 +	free(buf);
   5.336 +	return -1;
   5.337 +}
   5.338 +
   5.339 +RAZOR_EXPORT int
   5.340 +razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
   5.341 +			 const char *newpath)
   5.342 +{
   5.343 +	wchar_t *oldbuf, *newbuf;
   5.344 +	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
   5.345 +
   5.346 +	if (razor_atomic_in_error_state(atomic))
   5.347 +		return -1;
   5.348 +
   5.349 +	newbuf = razor_utf8_to_utf16(newpath, -1);
   5.350 +	oldbuf = razor_utf8_to_utf16(oldpath, -1);
   5.351 +
   5.352 +	/*
   5.353 +	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will
   5.354 +	 * cover every case we care about _except_ replacing an empty
   5.355 +	 * directory with a file. Calling RemoveDirectoryTransacted() will deal
   5.356 +	 * with this case while having no effect in all other cases.
   5.357 +	 */
   5.358 +	(void)RemoveDirectoryTransactedW(newbuf, atomic->transaction);
   5.359 +
   5.360 +	if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags,
   5.361 +			         atomic->transaction))
   5.362 +		razor_atomic_set_error(atomic, newbuf, GetLastError());
   5.363 +
   5.364 +	free(newbuf);
   5.365 +	free(oldbuf);
   5.366 +
   5.367 +	return !!atomic->error_str;
   5.368 +}
   5.369 +
   5.370 +RAZOR_EXPORT int
   5.371 +razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
   5.372 +			mode_t mode)
   5.373 +{
   5.374 +	wchar_t *buf;
   5.375 +	DWORD err;
   5.376 +	WIN32_FILE_ATTRIBUTE_DATA fa;
   5.377 +
   5.378 +	if (razor_atomic_in_error_state(atomic))
   5.379 +		return -1;
   5.380 +
   5.381 +	buf = razor_utf8_to_utf16(dirname, -1);
   5.382 +
   5.383 +	if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) {
   5.384 +		err = GetLastError();
   5.385 +		if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) {
   5.386 +abort:
   5.387 +			razor_atomic_set_error(atomic, buf, err);
   5.388 +			free(buf);
   5.389 +			return -1;
   5.390 +		}
   5.391 +
   5.392 +		if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard,
   5.393 +						  &fa, atomic->transaction))
   5.394 +			goto abort;
   5.395 +
   5.396 +		if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) {
   5.397 +			if (razor_atomic_remove(atomic, dirname)) {
   5.398 +				free(buf);
   5.399 +				return -1;
   5.400 +			}
   5.401 +			if (!CreateDirectoryTransactedW(NULL, buf, NULL,
   5.402 +							atomic->transaction)) {
   5.403 +				err = GetLastError();
   5.404 +				goto abort;
   5.405 +			}
   5.406 +		}
   5.407 +	}
   5.408 +
   5.409 +	free(buf);
   5.410 +
   5.411 +	return 0;
   5.412 +}
   5.413 +
   5.414 +RAZOR_EXPORT int
   5.415 +razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
   5.416 +			    const char *path)
   5.417 +{
   5.418 +	if (razor_atomic_in_error_state(atomic))
   5.419 +		return -1;
   5.420 +
   5.421 +	/*
   5.422 +	 * This isn't true, but symbolic links under Windows 7
   5.423 +	 * need to know whether the target is a directory or not
   5.424 +	 * and we don't always know that at the time when the
   5.425 +	 * link is created, so it's a convienent lie for now.
   5.426 +	 */
   5.427 +	razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported "
   5.428 +						 "on this platform");
   5.429 +
   5.430 +	return -1;
   5.431 +}
   5.432 +
   5.433 +RAZOR_EXPORT int
   5.434 +razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
   5.435 +			 mode_t mode)
   5.436 +{
   5.437 +	DWORD attribs;
   5.438 +	struct razor_atomic_file *files;
   5.439 +	int i = atomic->n_files;
   5.440 +
   5.441 +	if (razor_atomic_in_error_state(atomic))
   5.442 +		return -1;
   5.443 +
   5.444 +	files = realloc(atomic->files,
   5.445 +			(atomic->n_files+1) * sizeof(struct razor_atomic_file));
   5.446 +	if (!files) {
   5.447 +		razor_atomic_set_error_str(atomic, NULL, "Not enough memory");
   5.448 +		return -1;
   5.449 +	}
   5.450 +	atomic->n_files++;
   5.451 +	atomic->files = files;
   5.452 +
   5.453 +	files[i].path = razor_utf8_to_utf16(filename, -1);
   5.454 +
   5.455 +	/*
   5.456 +	 * Passing CREATE_ALWAYS to CreateFileTransacted() will cover
   5.457 +	 * every case we care about _except_ replacing an empty directory
   5.458 +	 * with a file. Calling RemoveDirectoryTransacted() will deal
   5.459 +	 * with this case while having no effect in all other cases.
   5.460 +	 */
   5.461 +	(void)RemoveDirectoryTransactedW(files[i].path, atomic->transaction);
   5.462 +
   5.463 +	if (mode & S_IWUSR)
   5.464 +		attribs = FILE_ATTRIBUTE_NORMAL;
   5.465 +	else
   5.466 +		attribs = FILE_ATTRIBUTE_READONLY;
   5.467 +
   5.468 +	files[i].h = CreateFileTransactedW(files[i].path, GENERIC_WRITE,
   5.469 +					   0, NULL, CREATE_ALWAYS, attribs,
   5.470 +					   NULL, atomic->transaction, NULL,
   5.471 +					   NULL);
   5.472 +
   5.473 +	if (files[i].h == INVALID_HANDLE_VALUE) {
   5.474 +		razor_atomic_set_error(atomic, files[i].path, GetLastError());
   5.475 +		free(files[i].path);
   5.476 +		atomic->n_files--;
   5.477 +		return -1;
   5.478 +	}
   5.479 +
   5.480 +	return i;
   5.481 +}
   5.482 +
   5.483 +RAZOR_EXPORT int
   5.484 +razor_atomic_write(struct razor_atomic *atomic, int handle, const void *data,
   5.485 +		   size_t size)
   5.486 +{
   5.487 +	DWORD written;
   5.488 +
   5.489 +	if (razor_atomic_in_error_state(atomic))
   5.490 +		return -1;
   5.491 +
   5.492 +	assert(handle < atomic->n_files);
   5.493 +	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
   5.494 +
   5.495 +	while(size) {
   5.496 +		if (!WriteFile(atomic->files[handle].h, data, size, &written,
   5.497 +			       NULL)) {
   5.498 +			razor_atomic_set_error(atomic,
   5.499 +					       atomic->files[handle].path,
   5.500 +					       GetLastError());
   5.501 +
   5.502 +			(void)CloseHandle(atomic->files[handle].h);
   5.503 +			free(atomic->files[handle].path);
   5.504 +			atomic->files[handle].path = NULL;
   5.505 +			atomic->files[handle].h = INVALID_HANDLE_VALUE;
   5.506 +
   5.507 +			return -1;
   5.508 +		}
   5.509 +
   5.510 +		data += written;
   5.511 +		size -= written;
   5.512 +	}
   5.513 +
   5.514 +	return 0;
   5.515 +}
   5.516 +
   5.517 +RAZOR_EXPORT int
   5.518 +razor_atomic_sync(struct razor_atomic *atomic, int handle)
   5.519 +{
   5.520 +	HANDLE h;
   5.521 +
   5.522 +	if (razor_atomic_in_error_state(atomic))
   5.523 +		return -1;
   5.524 +
   5.525 +	assert(handle < atomic->n_files);
   5.526 +	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
   5.527 +
   5.528 +	if (!CloseHandle(atomic->files[handle].h)) {
   5.529 +		razor_atomic_set_error(atomic, atomic->files[handle].path,
   5.530 +				       GetLastError());
   5.531 +		free(atomic->files[handle].path);
   5.532 +		atomic->files[handle].path = NULL;
   5.533 +		atomic->files[handle].h = INVALID_HANDLE_VALUE;
   5.534 +		return -1;
   5.535 +	}
   5.536 +
   5.537 +	h = CreateFileTransactedW(atomic->files[handle].path, GENERIC_WRITE, 0,
   5.538 +				  NULL, OPEN_EXISTING, 0, NULL,
   5.539 +				  atomic->transaction, NULL, NULL);
   5.540 +	atomic->files[handle].h = h;
   5.541 +
   5.542 +	if (atomic->files[handle].h == INVALID_HANDLE_VALUE) {
   5.543 +		razor_atomic_set_error(atomic, atomic->files[handle].path,
   5.544 +				       GetLastError());
   5.545 +		free(atomic->files[handle].path);
   5.546 +		atomic->files[handle].path = NULL;
   5.547 +		return -1;
   5.548 +	}
   5.549 +
   5.550 +	return !!atomic->error_str;
   5.551 +}
   5.552 +
   5.553 +RAZOR_EXPORT int
   5.554 +razor_atomic_close(struct razor_atomic *atomic, int handle)
   5.555 +{
   5.556 +	if (razor_atomic_in_error_state(atomic))
   5.557 +		return -1;
   5.558 +
   5.559 +	assert(handle < atomic->n_files);
   5.560 +	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
   5.561 +
   5.562 +	if (!CloseHandle(atomic->files[handle].h))
   5.563 +		razor_atomic_set_error(atomic, atomic->files[handle].path,
   5.564 +				       GetLastError());
   5.565 +
   5.566 +	free(atomic->files[handle].path);
   5.567 +	atomic->files[handle].path = NULL;
   5.568 +	atomic->files[handle].h = INVALID_HANDLE_VALUE;
   5.569 +
   5.570 +	while(atomic->n_files > 0 &&
   5.571 +	      atomic->files[atomic->n_files-1].h == INVALID_HANDLE_VALUE)
   5.572 +		atomic->n_files--;
   5.573 +
   5.574 +	return !!atomic->error_str;
   5.575 +}
   5.576 +
   5.577 +#endif		/* HAVE_WINDOWS_KTM */
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/librazor/atomic-none.c	Thu Feb 09 20:42:08 2012 +0000
     6.3 @@ -0,0 +1,249 @@
     6.4 +/*
     6.5 + * Copyright (C) 2011-2012  J. Ali Harlow <ali@juiblex.co.uk>
     6.6 + *
     6.7 + * This program is free software; you can redistribute it and/or modify
     6.8 + * it under the terms of the GNU General Public License as published by
     6.9 + * the Free Software Foundation; either version 2 of the License, or
    6.10 + * (at your option) any later version.
    6.11 + *
    6.12 + * This program is distributed in the hope that it will be useful,
    6.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    6.15 + * GNU General Public License for more details.
    6.16 + *
    6.17 + * You should have received a copy of the GNU General Public License along
    6.18 + * with this program; if not, write to the Free Software Foundation, Inc.,
    6.19 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    6.20 + */
    6.21 +
    6.22 +#include "config.h"
    6.23 +
    6.24 +#if !ENABLE_ATOMIC
    6.25 +
    6.26 +#include <stdlib.h>
    6.27 +#ifdef MSWIN_API
    6.28 +#include <windows.h>
    6.29 +#endif
    6.30 +#include <stdio.h>
    6.31 +#include <limits.h>
    6.32 +#include <errno.h>
    6.33 +#include <unistd.h>
    6.34 +#include <fcntl.h>
    6.35 +#include <sys/stat.h>
    6.36 +#include <string.h>
    6.37 +#include <assert.h>
    6.38 +
    6.39 +#include "razor.h"
    6.40 +#include "razor-internal.h"
    6.41 +
    6.42 +#ifndef O_BINARY
    6.43 +#define O_BINARY	0
    6.44 +#endif
    6.45 +
    6.46 +RAZOR_EXPORT struct razor_atomic *
    6.47 +razor_atomic_open(const char *description)
    6.48 +{
    6.49 +	struct razor_atomic *atomic;
    6.50 +
    6.51 +	atomic = zalloc(sizeof *atomic);
    6.52 +
    6.53 +	return atomic;
    6.54 +}
    6.55 +
    6.56 +RAZOR_EXPORT int
    6.57 +razor_atomic_commit(struct razor_atomic *atomic)
    6.58 +{
    6.59 +	return razor_atomic_in_error_state(atomic);
    6.60 +}
    6.61 +
    6.62 +RAZOR_EXPORT void
    6.63 +razor_atomic_destroy(struct razor_atomic *atomic)
    6.64 +{
    6.65 +	free(atomic->error_path);
    6.66 +	free(atomic->error_str);
    6.67 +	free(atomic->error_msg);
    6.68 +	free(atomic);
    6.69 +}
    6.70 +
    6.71 +RAZOR_EXPORT int
    6.72 +razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
    6.73 +		       const char *path)
    6.74 +{
    6.75 +	char buffer[PATH_MAX], *p;
    6.76 +	const char *slash, *next;
    6.77 +	struct stat buf;
    6.78 +
    6.79 +	if (razor_atomic_in_error_state(atomic))
    6.80 +		return -1;
    6.81 +
    6.82 +	strcpy(buffer, root);
    6.83 +	p = buffer + strlen(buffer);
    6.84 +	slash = path;
    6.85 +	for (slash = path; *slash != '\0'; slash = next) {
    6.86 +#ifdef MSWIN_API
    6.87 +		next = strpbrk(slash + 1, "/\\");
    6.88 +#else
    6.89 +		next = strchr(slash + 1, '/');
    6.90 +#endif
    6.91 +		if (next == NULL)
    6.92 +			break;
    6.93 +
    6.94 +		memcpy(p, slash, next - slash);
    6.95 +		p += next - slash;
    6.96 +		*p = '\0';
    6.97 +
    6.98 +		if (razor_valid_root_name(buffer))
    6.99 +			continue;
   6.100 +
   6.101 +		if (stat(buffer, &buf) == 0) {
   6.102 +			if (!S_ISDIR(buf.st_mode)) {
   6.103 +				razor_atomic_set_error_str(atomic, buffer,
   6.104 +							   "Not a directory");
   6.105 +				return -1;
   6.106 +			}
   6.107 +		} else if (mkdir(buffer, 0777) < 0) {
   6.108 +			razor_atomic_set_error_str(atomic, buffer,
   6.109 +						   strerror(errno));
   6.110 +			return -1;
   6.111 +		}
   6.112 +	}
   6.113 +
   6.114 +	return 0;
   6.115 +}
   6.116 +
   6.117 +RAZOR_EXPORT int
   6.118 +razor_atomic_remove(struct razor_atomic *atomic, const char *path)
   6.119 +{
   6.120 +#ifdef MSWIN_API
   6.121 +	wchar_t *buf;
   6.122 +	DWORD err;
   6.123 +#endif
   6.124 +
   6.125 +	if (razor_atomic_in_error_state(atomic))
   6.126 +		return -1;
   6.127 +
   6.128 +#ifdef MSWIN_API
   6.129 +	buf = razor_utf8_to_utf16(path, -1);
   6.130 +
   6.131 +	if (!DeleteFileW(buf)) {
   6.132 +		err = GetLastError();
   6.133 +		if (err != ERROR_FILE_NOT_FOUND &&
   6.134 +		    err != ERROR_PATH_NOT_FOUND &&
   6.135 +		    !(SetFileAttributesW(buf, FILE_ATTRIBUTE_NORMAL) &&
   6.136 +		      DeleteFileW(buf)) &&
   6.137 +		    !RemoveDirectoryW(buf) &&
   6.138 +		    GetLastError() != ERROR_DIR_NOT_EMPTY)
   6.139 +			razor_atomic_set_error_mswin(atomic, buf, err);
   6.140 +	}
   6.141 +
   6.142 +	free(buf);
   6.143 +#else
   6.144 +	if (remove(path))
   6.145 +		razor_atomic_set_error_str(atomic, path, strerror(errno));
   6.146 +#endif
   6.147 +
   6.148 +	return !!atomic->error_str;
   6.149 +}
   6.150 +
   6.151 +RAZOR_EXPORT int
   6.152 +razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
   6.153 +			 const char *newpath)
   6.154 +{
   6.155 +#ifdef MSWIN_API
   6.156 +	wchar_t *oldbuf, *newbuf;
   6.157 +	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
   6.158 +#endif
   6.159 +
   6.160 +	if (razor_atomic_in_error_state(atomic))
   6.161 +		return -1;
   6.162 +
   6.163 +#ifdef MSWIN_API
   6.164 +	newbuf = razor_utf8_to_utf16(newpath, -1);
   6.165 +	oldbuf = razor_utf8_to_utf16(oldpath, -1);
   6.166 +
   6.167 +	/*
   6.168 +	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
   6.169 +	 * cover every case we care about _except_ replacing an empty
   6.170 +	 * directory with a file. Calling RemoveDirectory() will deal
   6.171 +	 * with this case while having no effect in all other cases.
   6.172 +	 */
   6.173 +	(void)RemoveDirectoryW(newbuf);
   6.174 +
   6.175 +	if (!MoveFileExW(oldbuf, newbuf, flags))
   6.176 +		razor_atomic_set_error_mswin(atomic, newbuf, GetLastError());
   6.177 +
   6.178 +	free(newbuf);
   6.179 +	free(oldbuf);
   6.180 +#else
   6.181 +	if (rename(oldpath, newpath))
   6.182 +		razor_atomic_set_error_str(atomic, newpath, strerror(errno));
   6.183 +#endif
   6.184 +
   6.185 +	return !!atomic->error_str;
   6.186 +}
   6.187 +
   6.188 +RAZOR_EXPORT int
   6.189 +razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
   6.190 +			mode_t mode)
   6.191 +{
   6.192 +	if (razor_atomic_in_error_state(atomic))
   6.193 +		return -1;
   6.194 +
   6.195 +	if (!mkdir(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)))
   6.196 +		return 0;
   6.197 +
   6.198 +	if (errno != EEXIST) {
   6.199 +		razor_atomic_set_error_str(atomic, dirname, strerror(errno));
   6.200 +		return -1;
   6.201 +	}
   6.202 +
   6.203 +	if (chmod(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)) < 0) {
   6.204 +		razor_atomic_set_error_str(atomic, dirname, strerror(errno));
   6.205 +		return -1;
   6.206 +	}
   6.207 +
   6.208 +	return 0;
   6.209 +}
   6.210 +
   6.211 +RAZOR_EXPORT int
   6.212 +razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
   6.213 +			    const char *path)
   6.214 +{
   6.215 +	if (razor_atomic_in_error_state(atomic))
   6.216 +		return -1;
   6.217 +
   6.218 +#if HAVE_SYMLINK
   6.219 +	if (symlink(target, path) < 0) {
   6.220 +		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
   6.221 +		return -1;
   6.222 +	}
   6.223 +
   6.224 +	return 0;
   6.225 +#else
   6.226 +	razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported "
   6.227 +						 "on this platform");
   6.228 +
   6.229 +	return -1;
   6.230 +#endif
   6.231 +}
   6.232 +
   6.233 +RAZOR_EXPORT int
   6.234 +razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
   6.235 +			 mode_t mode)
   6.236 +{
   6.237 +	int fd;
   6.238 +
   6.239 +	if (razor_atomic_in_error_state(atomic))
   6.240 +		return -1;
   6.241 +
   6.242 +	atomic->error_path = strdup(filename);
   6.243 +	fd = open(atomic->error_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
   6.244 +		  mode & (S_IRWXU | S_IRWXG | S_IRWXO));
   6.245 +
   6.246 +	if (fd == -1)
   6.247 +		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
   6.248 +
   6.249 +	return fd;
   6.250 +}
   6.251 +
   6.252 +#endif	/* !ENABLE_ATOMIC */
     7.1 --- a/librazor/atomic.c	Thu Feb 09 20:15:00 2012 +0000
     7.2 +++ b/librazor/atomic.c	Thu Feb 09 20:42:08 2012 +0000
     7.3 @@ -1,5 +1,5 @@
     7.4  /*
     7.5 - * Copyright (C) 2011  J. Ali Harlow <ali@juiblex.co.uk>
     7.6 + * Copyright (C) 2011-2012  J. Ali Harlow <ali@juiblex.co.uk>
     7.7   *
     7.8   * This program is free software; you can redistribute it and/or modify
     7.9   * it under the terms of the GNU General Public License as published by
    7.10 @@ -19,21 +19,15 @@
    7.11  #include "config.h"
    7.12  
    7.13  #include <stdlib.h>
    7.14 -#ifdef MSWIN_API
    7.15 -#include <windows.h>
    7.16 -#endif
    7.17  #include <stdio.h>
    7.18  #include <limits.h>
    7.19  #include <errno.h>
    7.20  #include <unistd.h>
    7.21 +#include <sys/types.h>
    7.22 +#include <sys/stat.h>
    7.23  #include <fcntl.h>
    7.24 -#include <sys/stat.h>
    7.25  #include <string.h>
    7.26  #include <assert.h>
    7.27 -#if HAVE_WINDOWS_KTM
    7.28 -#include <wchar.h>
    7.29 -#include <ktmw32.h>
    7.30 -#endif
    7.31  
    7.32  #include "razor.h"
    7.33  #include "razor-internal.h"
    7.34 @@ -42,13 +36,6 @@
    7.35   * Atomic transactions
    7.36   */
    7.37  
    7.38 -#ifndef O_BINARY
    7.39 -#define O_BINARY	0
    7.40 -#endif
    7.41 -
    7.42 -#define RAZOR_ASCII_ISALPHA(c)	\
    7.43 -			((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z')
    7.44 -
    7.45  static int allow_all_root_names = 0;
    7.46  
    7.47  /*
    7.48 @@ -60,918 +47,12 @@
    7.49  	allow_all_root_names = disable;
    7.50  }
    7.51  
    7.52 -#ifdef MSWIN_API
    7.53 -
    7.54 -static char *
    7.55 -razor_utf16_to_utf8(const wchar_t *utf16, int len)
    7.56 +int
    7.57 +razor_allow_all_root_names(void)
    7.58  {
    7.59 -	int n;
    7.60 -	char *utf8;
    7.61 -
    7.62 -	n = WideCharToMultiByte(CP_UTF8, 0, utf16, len, NULL, 0, NULL, NULL);
    7.63 -	if (len >= 0 && utf16[len])
    7.64 -		n++;
    7.65 -	utf8 = malloc(n);
    7.66 -	(void)WideCharToMultiByte(CP_UTF8, 0, utf16, len, utf8, n, NULL, NULL);
    7.67 -	if (len >= 0 && utf16[len])
    7.68 -		utf8[n - 1] = 0;
    7.69 -
    7.70 -	return utf8;
    7.71 +	return allow_all_root_names;
    7.72  }
    7.73  
    7.74 -static wchar_t *
    7.75 -razor_utf8_to_utf16(const char *utf8, int len)
    7.76 -{
    7.77 -	int n;
    7.78 -	wchar_t *utf16;
    7.79 -
    7.80 -	n = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0);
    7.81 -	if (len >= 0 && utf8[len])
    7.82 -		n++;
    7.83 -	utf16 = malloc(n * sizeof(wchar_t));
    7.84 -	(void)MultiByteToWideChar(CP_UTF8, 0, utf8, len, utf16, n);
    7.85 -	if (len >= 0 && utf8[len])
    7.86 -		utf16[n - 1] = 0;
    7.87 -
    7.88 -	return utf16;
    7.89 -}
    7.90 -
    7.91 -#endif	/* MSWIN_API */
    7.92 -
    7.93 -#if HAVE_WINDOWS_KTM
    7.94 -
    7.95 -static int
    7.96 -razor_valid_root_name(const wchar_t *name)
    7.97 -{
    7.98 -	if (allow_all_root_names)
    7.99 -		return !wcschr(name, '/');
   7.100 -
   7.101 -	return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
   7.102 -	       name[2] == '\0';
   7.103 -}
   7.104 -
   7.105 -struct razor_atomic {
   7.106 -	HANDLE transaction;
   7.107 -	int n_files;
   7.108 -	struct razor_atomic_file {
   7.109 -		wchar_t *path;
   7.110 -		HANDLE h;
   7.111 -	} *files;
   7.112 -	char *error_path;
   7.113 -	char *error_str;
   7.114 -	char *error_msg;
   7.115 -};
   7.116 -
   7.117 -struct razor_wstr {
   7.118 -	wchar_t *str;
   7.119 -	int len, allocated;
   7.120 -};
   7.121 -
   7.122 -static struct razor_wstr *
   7.123 -razor_wstr_create(const char *init, int len)
   7.124 -{
   7.125 -	int n;
   7.126 -	struct razor_wstr *wstr;
   7.127 -
   7.128 -	wstr = malloc(sizeof(struct razor_wstr));
   7.129 -
   7.130 -	n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0);
   7.131 -	if (len >= 0 && init[len])
   7.132 -		wstr->len = n++;
   7.133 -	else
   7.134 -		wstr->len = n - 1;
   7.135 -
   7.136 -	wstr->allocated = n * 2;
   7.137 -	wstr->str = malloc(wstr->allocated * sizeof(wchar_t));
   7.138 -	if (!wstr->str) {
   7.139 -		free(wstr);
   7.140 -		return NULL;
   7.141 -	}
   7.142 -
   7.143 -	(void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n);
   7.144 -	if (len >= 0 && init[len])
   7.145 -		wstr->str[wstr->len] = 0;
   7.146 -
   7.147 -	return wstr;
   7.148 -}
   7.149 -
   7.150 -static int
   7.151 -razor_wstr_append(struct razor_wstr *wstr, const char *s, int len)
   7.152 -{
   7.153 -	int n, allocated;
   7.154 -	wchar_t *str;
   7.155 -
   7.156 -	n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0);
   7.157 -	if (len < 0 || !s[len])
   7.158 -		n--;
   7.159 -
   7.160 -	if (wstr->allocated <= wstr->len + n) {
   7.161 -		allocated = (wstr->len + n + 1) * 2;
   7.162 -		str = realloc(wstr->str, allocated * sizeof(wchar_t));
   7.163 -		if (!str)
   7.164 -			return -1;
   7.165 -		wstr->allocated = allocated;
   7.166 -		wstr->str = str;
   7.167 -	}
   7.168 -
   7.169 -	(void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n);
   7.170 -	wstr->len += n;
   7.171 -	wstr->str[wstr->len] = 0;
   7.172 -
   7.173 -	return 0;
   7.174 -}
   7.175 -
   7.176 -static void
   7.177 -razor_wstr_destroy(struct razor_wstr *wstr)
   7.178 -{
   7.179 -	free(wstr->str);
   7.180 -	free(wstr);
   7.181 -}
   7.182 -
   7.183 -RAZOR_EXPORT struct razor_atomic *
   7.184 -razor_atomic_open(const char *description)
   7.185 -{
   7.186 -	wchar_t *buf;
   7.187 -	struct razor_atomic *atomic;
   7.188 -
   7.189 -	atomic = zalloc(sizeof *atomic);
   7.190 -	buf = razor_utf8_to_utf16(description, -1);
   7.191 -	atomic->transaction = CreateTransaction(NULL, 0,
   7.192 -						TRANSACTION_DO_NOT_PROMOTE,
   7.193 -						0, 0, 0, buf);
   7.194 -	free(buf);
   7.195 -
   7.196 -	return atomic;
   7.197 -}
   7.198 -
   7.199 -static void
   7.200 -razor_atomic_set_error_str(struct razor_atomic *atomic, const wchar_t *path,
   7.201 -			   const char *str)
   7.202 -{
   7.203 -	assert(!atomic->error_str);
   7.204 -
   7.205 -	free(atomic->error_path);
   7.206 -
   7.207 -	if (path)
   7.208 -		atomic->error_path = razor_utf16_to_utf8(path, -1);
   7.209 -	else
   7.210 -		atomic->error_path = NULL;
   7.211 -
   7.212 -	atomic->error_str = strdup(str);
   7.213 -}
   7.214 -
   7.215 -static void
   7.216 -razor_atomic_set_error(struct razor_atomic *atomic, const wchar_t *path,
   7.217 -		       DWORD error)
   7.218 -{
   7.219 -	wchar_t *buf;
   7.220 -
   7.221 -	assert(!atomic->error_str);
   7.222 -
   7.223 -	free(atomic->error_path);
   7.224 -
   7.225 -	if (path)
   7.226 -		atomic->error_path = razor_utf16_to_utf8(path, -1);
   7.227 -	else
   7.228 -		atomic->error_path = NULL;
   7.229 -
   7.230 -	FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|
   7.231 -		       FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
   7.232 -		       NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
   7.233 -		       (LPWSTR)&buf, 0, NULL);
   7.234 -	atomic->error_str = razor_utf16_to_utf8(buf, -1);
   7.235 -	LocalFree(buf);
   7.236 -}
   7.237 -
   7.238 -RAZOR_EXPORT int
   7.239 -razor_atomic_commit(struct razor_atomic *atomic)
   7.240 -{
   7.241 -	int retval;
   7.242 -
   7.243 -	if (atomic->error_str)
   7.244 -		return -1;
   7.245 -
   7.246 -	retval = !CommitTransaction(atomic->transaction);
   7.247 -
   7.248 -	if (retval) {
   7.249 -		razor_atomic_set_error(atomic, NULL, GetLastError());
   7.250 -		RollbackTransaction(atomic->transaction);
   7.251 -	}
   7.252 -
   7.253 -	CloseHandle(atomic->transaction);
   7.254 -	atomic->transaction = INVALID_HANDLE_VALUE;
   7.255 -
   7.256 -	return retval;
   7.257 -}
   7.258 -
   7.259 -RAZOR_EXPORT void
   7.260 -razor_atomic_destroy(struct razor_atomic *atomic)
   7.261 -{
   7.262 -	int i;
   7.263 -
   7.264 -	for(i = 0; i < atomic->n_files; i++) {
   7.265 -		if (atomic->files[i].h != INVALID_HANDLE_VALUE) {
   7.266 -			CloseHandle(atomic->files[i].h);
   7.267 -			free(atomic->files[i].path);
   7.268 -		}
   7.269 -	}
   7.270 -	free(atomic->files);
   7.271 -	if (atomic->transaction != INVALID_HANDLE_VALUE) {
   7.272 -		RollbackTransaction(atomic->transaction);
   7.273 -		CloseHandle(atomic->transaction);
   7.274 -	}
   7.275 -	free(atomic->error_path);
   7.276 -	free(atomic->error_str);
   7.277 -	free(atomic->error_msg);
   7.278 -	free(atomic);
   7.279 -}
   7.280 -
   7.281 -RAZOR_EXPORT int
   7.282 -razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
   7.283 -		       const char *path)
   7.284 -{
   7.285 -	struct razor_wstr *buffer;
   7.286 -	const char *slash, *s, *next;
   7.287 -	WIN32_FILE_ATTRIBUTE_DATA fa;
   7.288 -	DWORD err;
   7.289 -	int r, creating = 0;
   7.290 -
   7.291 -	if (atomic->error_str)
   7.292 -		return -1;
   7.293 -
   7.294 -	buffer = razor_wstr_create(root, -1);
   7.295 -	slash = path;
   7.296 -
   7.297 -	for (; *slash != '\0'; slash = next) {
   7.298 -		next = strpbrk(slash + 1, "/\\");
   7.299 -		if (next == NULL)
   7.300 -			break;
   7.301 -
   7.302 -		razor_wstr_append(buffer, slash, next - slash);
   7.303 -
   7.304 -		if (!creating) {
   7.305 -			if (razor_valid_root_name(buffer->str))
   7.306 -				continue;
   7.307 -
   7.308 -			r = GetFileAttributesTransactedW(buffer->str,
   7.309 -							 GetFileExInfoStandard,
   7.310 -							 &fa,
   7.311 -							 atomic->transaction);
   7.312 -
   7.313 -			if (!r) {
   7.314 -				err = GetLastError();
   7.315 -				if (err == ERROR_FILE_NOT_FOUND) {
   7.316 -					creating = 1;
   7.317 -				} else {
   7.318 -					razor_atomic_set_error(atomic,
   7.319 -							       buffer->str,
   7.320 -							       err);
   7.321 -					razor_wstr_destroy(buffer);
   7.322 -					return -1;
   7.323 -				}
   7.324 -			} else if (!(fa.dwFileAttributes&
   7.325 -				     FILE_ATTRIBUTE_DIRECTORY)) {
   7.326 -				razor_atomic_set_error_str(atomic, buffer->str,
   7.327 -							   "Not a directory");
   7.328 -				razor_wstr_destroy(buffer);
   7.329 -				return -1;
   7.330 -			}
   7.331 -		}
   7.332 -		if (creating) {
   7.333 -			if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL,
   7.334 -							atomic->transaction)) {
   7.335 -				razor_atomic_set_error(atomic, buffer->str,
   7.336 -						       GetLastError());
   7.337 -				razor_wstr_destroy(buffer);
   7.338 -				return -1;
   7.339 -			}
   7.340 -
   7.341 -			/* FIXME: What to do about permissions for dirs we
   7.342 -			 * have to create but are not in the cpio archive? */
   7.343 -		}
   7.344 -	}
   7.345 -
   7.346 -	razor_wstr_destroy(buffer);
   7.347 -
   7.348 -	return 0;
   7.349 -}
   7.350 -
   7.351 -RAZOR_EXPORT int
   7.352 -razor_atomic_remove(struct razor_atomic *atomic, const char *path)
   7.353 -{
   7.354 -	wchar_t *buf;
   7.355 -	DWORD err;
   7.356 -
   7.357 -	if (atomic->error_str)
   7.358 -		return -1;
   7.359 -
   7.360 -	buf = razor_utf8_to_utf16(path, -1);
   7.361 -
   7.362 -	if (DeleteFileTransactedW(buf, atomic->transaction)) {
   7.363 -		free(buf);
   7.364 -		return 0;
   7.365 -	}
   7.366 -
   7.367 -	err = GetLastError();
   7.368 -	if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
   7.369 -		free(buf);
   7.370 -		return 0;
   7.371 -	}
   7.372 -
   7.373 -	if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL,
   7.374 -					 atomic->transaction)) {
   7.375 -		if (DeleteFileTransactedW(buf, atomic->transaction)) {
   7.376 -			free(buf);
   7.377 -			return 0;
   7.378 -		}
   7.379 -		err = GetLastError();
   7.380 -	}
   7.381 -
   7.382 -	if (RemoveDirectoryTransactedW(buf, atomic->transaction) ||
   7.383 -	    GetLastError() == ERROR_DIR_NOT_EMPTY) {
   7.384 -		free(buf);
   7.385 -		return 0;
   7.386 -	}
   7.387 -
   7.388 -	/*
   7.389 -	 * It would be tempting to use:
   7.390 -	 * 	MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)
   7.391 -	 * but unless we can guarantee that the system will be rebooted
   7.392 -	 * before we (or some other application) write another file with the
   7.393 -	 * same path, this is likely to cause more problems than it solves.
   7.394 -	 */
   7.395 -
   7.396 -	razor_atomic_set_error(atomic, buf, err);
   7.397 -	free(buf);
   7.398 -	return -1;
   7.399 -}
   7.400 -
   7.401 -RAZOR_EXPORT int
   7.402 -razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
   7.403 -			 const char *newpath)
   7.404 -{
   7.405 -	wchar_t *oldbuf, *newbuf;
   7.406 -	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
   7.407 -
   7.408 -	if (atomic->error_str)
   7.409 -		return -1;
   7.410 -
   7.411 -	newbuf = razor_utf8_to_utf16(newpath, -1);
   7.412 -	oldbuf = razor_utf8_to_utf16(oldpath, -1);
   7.413 -
   7.414 -	/*
   7.415 -	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will
   7.416 -	 * cover every case we care about _except_ replacing an empty
   7.417 -	 * directory with a file. Calling RemoveDirectoryTransacted() will deal
   7.418 -	 * with this case while having no effect in all other cases.
   7.419 -	 */
   7.420 -	(void)RemoveDirectoryTransactedW(newbuf, atomic->transaction);
   7.421 -
   7.422 -	if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags,
   7.423 -			         atomic->transaction))
   7.424 -		razor_atomic_set_error(atomic, newbuf, GetLastError());
   7.425 -
   7.426 -	free(newbuf);
   7.427 -	free(oldbuf);
   7.428 -
   7.429 -	return !!atomic->error_str;
   7.430 -}
   7.431 -
   7.432 -RAZOR_EXPORT int
   7.433 -razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
   7.434 -			mode_t mode)
   7.435 -{
   7.436 -	wchar_t *buf;
   7.437 -	DWORD err;
   7.438 -	WIN32_FILE_ATTRIBUTE_DATA fa;
   7.439 -
   7.440 -	if (atomic->error_str)
   7.441 -		return -1;
   7.442 -
   7.443 -	buf = razor_utf8_to_utf16(dirname, -1);
   7.444 -
   7.445 -	if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) {
   7.446 -		err = GetLastError();
   7.447 -		if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) {
   7.448 -abort:
   7.449 -			razor_atomic_set_error(atomic, buf, err);
   7.450 -			free(buf);
   7.451 -			return -1;
   7.452 -		}
   7.453 -
   7.454 -		if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard,
   7.455 -						  &fa, atomic->transaction))
   7.456 -			goto abort;
   7.457 -
   7.458 -		if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) {
   7.459 -			if (razor_atomic_remove(atomic, dirname)) {
   7.460 -				free(buf);
   7.461 -				return -1;
   7.462 -			}
   7.463 -			if (!CreateDirectoryTransactedW(NULL, buf, NULL,
   7.464 -							atomic->transaction)) {
   7.465 -				err = GetLastError();
   7.466 -				goto abort;
   7.467 -			}
   7.468 -		}
   7.469 -	}
   7.470 -
   7.471 -	free(buf);
   7.472 -
   7.473 -	return 0;
   7.474 -}
   7.475 -
   7.476 -RAZOR_EXPORT int
   7.477 -razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
   7.478 -			    const char *path)
   7.479 -{
   7.480 -	if (atomic->error_str)
   7.481 -		return -1;
   7.482 -
   7.483 -	/*
   7.484 -	 * This isn't true, but symbolic links under Windows 7
   7.485 -	 * need to know whether the target is a directory or not
   7.486 -	 * and we don't always know that at the time when the
   7.487 -	 * link is created, so it's a convienent lie for now.
   7.488 -	 */
   7.489 -	razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported "
   7.490 -						 "on this platform");
   7.491 -
   7.492 -	return -1;
   7.493 -}
   7.494 -
   7.495 -RAZOR_EXPORT int
   7.496 -razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
   7.497 -			 mode_t mode)
   7.498 -{
   7.499 -	DWORD attribs;
   7.500 -	struct razor_atomic_file *files;
   7.501 -	int i = atomic->n_files;
   7.502 -
   7.503 -	if (atomic->error_str)
   7.504 -		return -1;
   7.505 -
   7.506 -	files = realloc(atomic->files,
   7.507 -			(atomic->n_files+1) * sizeof(struct razor_atomic_file));
   7.508 -	if (!files) {
   7.509 -		razor_atomic_set_error_str(atomic, NULL, "Not enough memory");
   7.510 -		return -1;
   7.511 -	}
   7.512 -	atomic->n_files++;
   7.513 -	atomic->files = files;
   7.514 -
   7.515 -	files[i].path = razor_utf8_to_utf16(filename, -1);
   7.516 -
   7.517 -	/*
   7.518 -	 * Passing CREATE_ALWAYS to CreateFileTransacted() will cover
   7.519 -	 * every case we care about _except_ replacing an empty directory
   7.520 -	 * with a file. Calling RemoveDirectoryTransacted() will deal
   7.521 -	 * with this case while having no effect in all other cases.
   7.522 -	 */
   7.523 -	(void)RemoveDirectoryTransactedW(files[i].path, atomic->transaction);
   7.524 -
   7.525 -	if (mode & S_IWUSR)
   7.526 -		attribs = FILE_ATTRIBUTE_NORMAL;
   7.527 -	else
   7.528 -		attribs = FILE_ATTRIBUTE_READONLY;
   7.529 -
   7.530 -	files[i].h = CreateFileTransactedW(files[i].path, GENERIC_WRITE,
   7.531 -					   0, NULL, CREATE_ALWAYS, attribs,
   7.532 -					   NULL, atomic->transaction, NULL,
   7.533 -					   NULL);
   7.534 -
   7.535 -	if (files[i].h == INVALID_HANDLE_VALUE) {
   7.536 -		razor_atomic_set_error(atomic, files[i].path, GetLastError());
   7.537 -		free(files[i].path);
   7.538 -		atomic->n_files--;
   7.539 -		return -1;
   7.540 -	}
   7.541 -
   7.542 -	return i;
   7.543 -}
   7.544 -
   7.545 -RAZOR_EXPORT int
   7.546 -razor_atomic_write(struct razor_atomic *atomic, int handle, const void *data,
   7.547 -		   size_t size)
   7.548 -{
   7.549 -	DWORD written;
   7.550 -
   7.551 -	if (atomic->error_str)
   7.552 -		return -1;
   7.553 -
   7.554 -	assert(handle < atomic->n_files);
   7.555 -	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
   7.556 -
   7.557 -	while(size) {
   7.558 -		if (!WriteFile(atomic->files[handle].h, data, size, &written,
   7.559 -			       NULL)) {
   7.560 -			razor_atomic_set_error(atomic,
   7.561 -					       atomic->files[handle].path,
   7.562 -					       GetLastError());
   7.563 -
   7.564 -			(void)CloseHandle(atomic->files[handle].h);
   7.565 -			free(atomic->files[handle].path);
   7.566 -			atomic->files[handle].path = NULL;
   7.567 -			atomic->files[handle].h = INVALID_HANDLE_VALUE;
   7.568 -
   7.569 -			return -1;
   7.570 -		}
   7.571 -
   7.572 -		data += written;
   7.573 -		size -= written;
   7.574 -	}
   7.575 -
   7.576 -	return 0;
   7.577 -}
   7.578 -
   7.579 -RAZOR_EXPORT int
   7.580 -razor_atomic_sync(struct razor_atomic *atomic, int handle)
   7.581 -{
   7.582 -	HANDLE h;
   7.583 -
   7.584 -	if (atomic->error_str)
   7.585 -		return -1;
   7.586 -
   7.587 -	assert(handle < atomic->n_files);
   7.588 -	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
   7.589 -
   7.590 -	if (!CloseHandle(atomic->files[handle].h)) {
   7.591 -		razor_atomic_set_error(atomic, atomic->files[handle].path,
   7.592 -				       GetLastError());
   7.593 -		free(atomic->files[handle].path);
   7.594 -		atomic->files[handle].path = NULL;
   7.595 -		atomic->files[handle].h = INVALID_HANDLE_VALUE;
   7.596 -		return -1;
   7.597 -	}
   7.598 -
   7.599 -	h = CreateFileTransactedW(atomic->files[handle].path, GENERIC_WRITE, 0,
   7.600 -				  NULL, OPEN_EXISTING, 0, NULL,
   7.601 -				  atomic->transaction, NULL, NULL);
   7.602 -	atomic->files[handle].h = h;
   7.603 -
   7.604 -	if (atomic->files[handle].h == INVALID_HANDLE_VALUE) {
   7.605 -		razor_atomic_set_error(atomic, atomic->files[handle].path,
   7.606 -				       GetLastError());
   7.607 -		free(atomic->files[handle].path);
   7.608 -		atomic->files[handle].path = NULL;
   7.609 -		return -1;
   7.610 -	}
   7.611 -
   7.612 -	return !!atomic->error_str;
   7.613 -}
   7.614 -
   7.615 -RAZOR_EXPORT int
   7.616 -razor_atomic_close(struct razor_atomic *atomic, int handle)
   7.617 -{
   7.618 -	if (atomic->error_str)
   7.619 -		return -1;
   7.620 -
   7.621 -	assert(handle < atomic->n_files);
   7.622 -	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
   7.623 -
   7.624 -	if (!CloseHandle(atomic->files[handle].h))
   7.625 -		razor_atomic_set_error(atomic, atomic->files[handle].path,
   7.626 -				       GetLastError());
   7.627 -
   7.628 -	free(atomic->files[handle].path);
   7.629 -	atomic->files[handle].path = NULL;
   7.630 -	atomic->files[handle].h = INVALID_HANDLE_VALUE;
   7.631 -
   7.632 -	while(atomic->n_files > 0 &&
   7.633 -	      atomic->files[atomic->n_files-1].h == INVALID_HANDLE_VALUE)
   7.634 -		atomic->n_files--;
   7.635 -
   7.636 -	return !!atomic->error_str;
   7.637 -}
   7.638 -
   7.639 -#else		/* HAVE_WINDOWS_KVM */
   7.640 -
   7.641 -static int
   7.642 -razor_valid_root_name(const char *name)
   7.643 -{
   7.644 -	if (allow_all_root_names) {
   7.645 -#ifdef MSWIN_API
   7.646 -		return !strpbrk(name, "/\\");
   7.647 -#else
   7.648 -		return !strchr(name, '/');
   7.649 -#endif
   7.650 -	}
   7.651 -
   7.652 -#ifdef MSWIN_API
   7.653 -	return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
   7.654 -	       name[2] == '\0';
   7.655 -#else
   7.656 -	return name[0] == '\0';
   7.657 -#endif
   7.658 -}
   7.659 -
   7.660 -struct razor_atomic {
   7.661 -	char *error_path;
   7.662 -	char *error_str;
   7.663 -	char *error_msg;
   7.664 -};
   7.665 -
   7.666 -RAZOR_EXPORT struct razor_atomic *
   7.667 -razor_atomic_open(const char *description)
   7.668 -{
   7.669 -	struct razor_atomic *atomic;
   7.670 -
   7.671 -	atomic = zalloc(sizeof *atomic);
   7.672 -
   7.673 -	return atomic;
   7.674 -}
   7.675 -
   7.676 -static void
   7.677 -razor_atomic_set_error_str(struct razor_atomic *atomic, const char *path,
   7.678 -			   const char *str)
   7.679 -{
   7.680 -	assert(!atomic->error_str);
   7.681 -
   7.682 -	atomic->error_path = path ? strdup(path) : NULL;
   7.683 -	atomic->error_str = strdup(str);
   7.684 -}
   7.685 -
   7.686 -#ifdef MSWIN_API
   7.687 -static void
   7.688 -razor_atomic_set_error_mswin(struct razor_atomic *atomic, const wchar_t *path,
   7.689 -		       DWORD error)
   7.690 -{
   7.691 -	wchar_t *buf;
   7.692 -
   7.693 -	assert(!atomic->error_str);
   7.694 -
   7.695 -	free(atomic->error_path);
   7.696 -
   7.697 -	if (path)
   7.698 -		atomic->error_path = razor_utf16_to_utf8(path, -1);
   7.699 -	else
   7.700 -		atomic->error_path = NULL;
   7.701 -
   7.702 -	FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|
   7.703 -		       FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
   7.704 -		       NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
   7.705 -		       (LPWSTR)&buf, 0, NULL);
   7.706 -	atomic->error_str = razor_utf16_to_utf8(buf, -1);
   7.707 -	LocalFree(buf);
   7.708 -}
   7.709 -#endif
   7.710 -
   7.711 -RAZOR_EXPORT int
   7.712 -razor_atomic_commit(struct razor_atomic *atomic)
   7.713 -{
   7.714 -	return !!atomic->error_str;
   7.715 -}
   7.716 -
   7.717 -RAZOR_EXPORT void
   7.718 -razor_atomic_destroy(struct razor_atomic *atomic)
   7.719 -{
   7.720 -	free(atomic->error_path);
   7.721 -	free(atomic->error_str);
   7.722 -	free(atomic->error_msg);
   7.723 -	free(atomic);
   7.724 -}
   7.725 -
   7.726 -RAZOR_EXPORT int
   7.727 -razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
   7.728 -		       const char *path)
   7.729 -{
   7.730 -	char buffer[PATH_MAX], *p;
   7.731 -	const char *slash, *next;
   7.732 -	struct stat buf;
   7.733 -
   7.734 -	if (atomic->error_str)
   7.735 -		return -1;
   7.736 -
   7.737 -	strcpy(buffer, root);
   7.738 -	p = buffer + strlen(buffer);
   7.739 -	slash = path;
   7.740 -	for (slash = path; *slash != '\0'; slash = next) {
   7.741 -#ifdef MSWIN_API
   7.742 -		next = strpbrk(slash + 1, "/\\");
   7.743 -#else
   7.744 -		next = strchr(slash + 1, '/');
   7.745 -#endif
   7.746 -		if (next == NULL)
   7.747 -			break;
   7.748 -
   7.749 -		memcpy(p, slash, next - slash);
   7.750 -		p += next - slash;
   7.751 -		*p = '\0';
   7.752 -
   7.753 -		if (razor_valid_root_name(buffer))
   7.754 -			continue;
   7.755 -
   7.756 -		if (stat(buffer, &buf) == 0) {
   7.757 -			if (!S_ISDIR(buf.st_mode)) {
   7.758 -				razor_atomic_set_error_str(atomic, buffer,
   7.759 -							   "Not a directory");
   7.760 -				return -1;
   7.761 -			}
   7.762 -		} else if (mkdir(buffer, 0777) < 0) {
   7.763 -			razor_atomic_set_error_str(atomic, buffer,
   7.764 -						   strerror(errno));
   7.765 -			return -1;
   7.766 -		}
   7.767 -	}
   7.768 -
   7.769 -	return 0;
   7.770 -}
   7.771 -
   7.772 -RAZOR_EXPORT int
   7.773 -razor_atomic_remove(struct razor_atomic *atomic, const char *path)
   7.774 -{
   7.775 -#ifdef MSWIN_API
   7.776 -	wchar_t *buf;
   7.777 -	DWORD err;
   7.778 -#endif
   7.779 -
   7.780 -	if (atomic->error_str)
   7.781 -		return -1;
   7.782 -
   7.783 -#ifdef MSWIN_API
   7.784 -	buf = razor_utf8_to_utf16(path, -1);
   7.785 -
   7.786 -	if (!DeleteFileW(buf)) {
   7.787 -		err = GetLastError();
   7.788 -		if (err != ERROR_FILE_NOT_FOUND &&
   7.789 -		    err != ERROR_PATH_NOT_FOUND &&
   7.790 -		    !(SetFileAttributesW(buf, FILE_ATTRIBUTE_NORMAL) &&
   7.791 -		      DeleteFileW(buf)) &&
   7.792 -		    !RemoveDirectoryW(buf) &&
   7.793 -		    GetLastError() != ERROR_DIR_NOT_EMPTY)
   7.794 -			razor_atomic_set_error_mswin(atomic, buf, err);
   7.795 -	}
   7.796 -
   7.797 -	free(buf);
   7.798 -#else
   7.799 -	if (remove(path))
   7.800 -		razor_atomic_set_error_str(atomic, path, strerror(errno));
   7.801 -#endif
   7.802 -
   7.803 -	return !!atomic->error_str;
   7.804 -}
   7.805 -
   7.806 -RAZOR_EXPORT int
   7.807 -razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
   7.808 -			 const char *newpath)
   7.809 -{
   7.810 -#ifdef MSWIN_API
   7.811 -	wchar_t *oldbuf, *newbuf;
   7.812 -	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
   7.813 -#endif
   7.814 -
   7.815 -	if (atomic->error_str)
   7.816 -		return -1;
   7.817 -
   7.818 -#ifdef MSWIN_API
   7.819 -	newbuf = razor_utf8_to_utf16(newpath, -1);
   7.820 -	oldbuf = razor_utf8_to_utf16(oldpath, -1);
   7.821 -
   7.822 -	/*
   7.823 -	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
   7.824 -	 * cover every case we care about _except_ replacing an empty
   7.825 -	 * directory with a file. Calling RemoveDirectory() will deal
   7.826 -	 * with this case while having no effect in all other cases.
   7.827 -	 */
   7.828 -	(void)RemoveDirectoryW(newbuf);
   7.829 -
   7.830 -	if (!MoveFileExW(oldbuf, newbuf, flags))
   7.831 -		razor_atomic_set_error_mswin(atomic, newbuf, GetLastError());
   7.832 -
   7.833 -	free(newbuf);
   7.834 -	free(oldbuf);
   7.835 -#else
   7.836 -	if (rename(oldpath, newpath))
   7.837 -		razor_atomic_set_error_str(atomic, newpath, strerror(errno));
   7.838 -#endif
   7.839 -
   7.840 -	return !!atomic->error_str;
   7.841 -}
   7.842 -
   7.843 -RAZOR_EXPORT int
   7.844 -razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
   7.845 -			mode_t mode)
   7.846 -{
   7.847 -	if (atomic->error_str)
   7.848 -		return -1;
   7.849 -
   7.850 -	if (!mkdir(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)))
   7.851 -		return 0;
   7.852 -
   7.853 -	if (errno != EEXIST) {
   7.854 -		razor_atomic_set_error_str(atomic, dirname, strerror(errno));
   7.855 -		return -1;
   7.856 -	}
   7.857 -
   7.858 -	if (chmod(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)) < 0) {
   7.859 -		razor_atomic_set_error_str(atomic, dirname, strerror(errno));
   7.860 -		return -1;
   7.861 -	}
   7.862 -
   7.863 -	return 0;
   7.864 -}
   7.865 -
   7.866 -RAZOR_EXPORT int
   7.867 -razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
   7.868 -			    const char *path)
   7.869 -{
   7.870 -	if (atomic->error_str)
   7.871 -		return -1;
   7.872 -
   7.873 -#if HAVE_SYMLINK
   7.874 -	if (symlink(target, path) < 0) {
   7.875 -		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
   7.876 -		return -1;
   7.877 -	}
   7.878 -#else
   7.879 -	razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported "
   7.880 -						 "on this platform");
   7.881 -#endif
   7.882 -
   7.883 -	return 0;
   7.884 -}
   7.885 -
   7.886 -RAZOR_EXPORT int
   7.887 -razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
   7.888 -			 mode_t mode)
   7.889 -{
   7.890 -	int fd;
   7.891 -
   7.892 -	if (atomic->error_str)
   7.893 -		return -1;
   7.894 -
   7.895 -	atomic->error_path = strdup(filename);
   7.896 -	fd = open(atomic->error_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
   7.897 -		  mode & (S_IRWXU | S_IRWXG | S_IRWXO));
   7.898 -
   7.899 -	if (fd == -1)
   7.900 -		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
   7.901 -
   7.902 -	return fd;
   7.903 -}
   7.904 -
   7.905 -RAZOR_EXPORT int
   7.906 -razor_atomic_write(struct razor_atomic *atomic, int fd, const void *data,
   7.907 -		   size_t size)
   7.908 -{
   7.909 -	int written;
   7.910 -
   7.911 -	if (atomic->error_str)
   7.912 -		return -1;
   7.913 -
   7.914 -	while(size) {
   7.915 -		written = write(fd, data, size);
   7.916 -		if (written < 0) {
   7.917 -			razor_atomic_set_error_str(atomic, NULL, strerror(errno));
   7.918 -
   7.919 -			(void)close(fd);
   7.920 -
   7.921 -			return -1;
   7.922 -		}
   7.923 -
   7.924 -		data += written;
   7.925 -		size -= written;
   7.926 -	}
   7.927 -
   7.928 -	return 0;
   7.929 -}
   7.930 -
   7.931 -RAZOR_EXPORT int
   7.932 -razor_atomic_sync(struct razor_atomic *atomic, int handle)
   7.933 -{
   7.934 -	if (atomic->error_str)
   7.935 -		return -1;
   7.936 -
   7.937 -	if (fsync(handle) < 0) {
   7.938 -		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
   7.939 -		return -1;
   7.940 -	}
   7.941 -
   7.942 -	free(atomic->error_path);
   7.943 -	atomic->error_path = NULL;
   7.944 -
   7.945 -	return 0;
   7.946 -}
   7.947 -
   7.948 -RAZOR_EXPORT int
   7.949 -razor_atomic_close(struct razor_atomic *atomic, int fd)
   7.950 -{
   7.951 -	if (atomic->error_str)
   7.952 -		return -1;
   7.953 -
   7.954 -	if (close(fd) < 0) {
   7.955 -		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
   7.956 -		return -1;
   7.957 -	}
   7.958 -
   7.959 -	free(atomic->error_path);
   7.960 -	atomic->error_path = NULL;
   7.961 -
   7.962 -	return 0;
   7.963 -}
   7.964 -
   7.965 -#endif		/* HAVE_WINDOWS_KVM */
   7.966 -
   7.967  RAZOR_EXPORT const char *
   7.968  razor_atomic_get_error_msg(struct razor_atomic *atomic)
   7.969  {
   7.970 @@ -998,5 +79,131 @@
   7.971  RAZOR_EXPORT int
   7.972  razor_atomic_in_error_state(struct razor_atomic *atomic)
   7.973  {
   7.974 -	return !!atomic->error_str;
   7.975 +	return atomic->error_str && !atomic->in_undo;
   7.976  }
   7.977 +
   7.978 +#if !HAVE_WINDOWS_KTM
   7.979 +
   7.980 +/*
   7.981 + * Common code with atomic-none and atomic-emulate
   7.982 + */
   7.983 +
   7.984 +#define RAZOR_ASCII_ISALPHA(c)	\
   7.985 +			((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z')
   7.986 +
   7.987 +int
   7.988 +razor_valid_root_name(const char *name)
   7.989 +{
   7.990 +	if (razor_allow_all_root_names()) {
   7.991 +#ifdef MSWIN_API
   7.992 +		return !strpbrk(name, "/\\");
   7.993 +#else
   7.994 +		return !strchr(name, '/');
   7.995 +#endif
   7.996 +	}
   7.997 +
   7.998 +#ifdef MSWIN_API
   7.999 +	return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
  7.1000 +	       name[2] == '\0';
  7.1001 +#else
  7.1002 +	return name[0] == '\0';
  7.1003 +#endif
  7.1004 +}
  7.1005 +
  7.1006 +#ifdef MSWIN_API
  7.1007 +void
  7.1008 +razor_atomic_set_error_mswin(struct razor_atomic *atomic, const wchar_t *path,
  7.1009 +			     DWORD error)
  7.1010 +{
  7.1011 +	wchar_t *buf;
  7.1012 +
  7.1013 +	assert(!atomic->error_str);
  7.1014 +
  7.1015 +	free(atomic->error_path);
  7.1016 +
  7.1017 +	if (path)
  7.1018 +		atomic->error_path = razor_utf16_to_utf8(path, -1);
  7.1019 +	else
  7.1020 +		atomic->error_path = NULL;
  7.1021 +
  7.1022 +	FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|
  7.1023 +		       FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
  7.1024 +		       NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
  7.1025 +		       (LPWSTR)&buf, 0, NULL);
  7.1026 +	atomic->error_str = razor_utf16_to_utf8(buf, -1);
  7.1027 +	LocalFree(buf);
  7.1028 +}
  7.1029 +#endif
  7.1030 +
  7.1031 +void
  7.1032 +razor_atomic_set_error_str(struct razor_atomic *atomic, const char *path,
  7.1033 +			   const char *str)
  7.1034 +{
  7.1035 +	assert(!atomic->error_str);
  7.1036 +
  7.1037 +	atomic->error_path = path ? strdup(path) : NULL;
  7.1038 +	atomic->error_str = strdup(str);
  7.1039 +}
  7.1040 +
  7.1041 +RAZOR_EXPORT int
  7.1042 +razor_atomic_write(struct razor_atomic *atomic, int fd, const void *data,
  7.1043 +		   size_t size)
  7.1044 +{
  7.1045 +	int written;
  7.1046 +
  7.1047 +	if (razor_atomic_in_error_state(atomic))
  7.1048 +		return -1;
  7.1049 +
  7.1050 +	while(size) {
  7.1051 +		written = write(fd, data, size);
  7.1052 +		if (written < 0) {
  7.1053 +			razor_atomic_set_error_str(atomic, NULL,
  7.1054 +						   strerror(errno));
  7.1055 +
  7.1056 +			(void)close(fd);
  7.1057 +
  7.1058 +			return -1;
  7.1059 +		}
  7.1060 +
  7.1061 +		data += written;
  7.1062 +		size -= written;
  7.1063 +	}
  7.1064 +
  7.1065 +	return 0;
  7.1066 +}
  7.1067 +
  7.1068 +RAZOR_EXPORT int
  7.1069 +razor_atomic_sync(struct razor_atomic *atomic, int handle)
  7.1070 +{
  7.1071 +	if (razor_atomic_in_error_state(atomic))
  7.1072 +		return -1;
  7.1073 +
  7.1074 +	if (fsync(handle) < 0) {
  7.1075 +		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
  7.1076 +		return -1;
  7.1077 +	}
  7.1078 +
  7.1079 +	free(atomic->error_path);
  7.1080 +	atomic->error_path = NULL;
  7.1081 +
  7.1082 +	return 0;
  7.1083 +}
  7.1084 +
  7.1085 +RAZOR_EXPORT int
  7.1086 +razor_atomic_close(struct razor_atomic *atomic, int fd)
  7.1087 +{
  7.1088 +	if (razor_atomic_in_error_state(atomic))
  7.1089 +		return -1;
  7.1090 +
  7.1091 +	if (close(fd) < 0) {
  7.1092 +		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
  7.1093 +		return -1;
  7.1094 +	}
  7.1095 +
  7.1096 +	free(atomic->error_path);
  7.1097 +	atomic->error_path = NULL;
  7.1098 +
  7.1099 +	return 0;
  7.1100 +}
  7.1101 +
  7.1102 +#endif	/* !HAVE_WINDOWS_KTM */
     8.1 --- a/librazor/razor-internal.h	Thu Feb 09 20:15:00 2012 +0000
     8.2 +++ b/librazor/razor-internal.h	Thu Feb 09 20:42:08 2012 +0000
     8.3 @@ -1,7 +1,7 @@
     8.4  /*
     8.5   * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     8.6   * Copyright (C) 2008  Red Hat, Inc
     8.7 - * Copyright (C) 2009, 2011  J. Ali Harlow <ali@juiblex.co.uk>
     8.8 + * Copyright (C) 2009, 2011-2012  J. Ali Harlow <ali@juiblex.co.uk>
     8.9   *
    8.10   * This program is free software; you can redistribute it and/or modify
    8.11   * it under the terms of the GNU General Public License as published by
    8.12 @@ -21,6 +21,9 @@
    8.13  #ifndef _RAZOR_INTERNAL_H_
    8.14  #define _RAZOR_INTERNAL_H_
    8.15  
    8.16 +#ifdef MSWIN_API
    8.17 +#include <windows.h>
    8.18 +#endif
    8.19  #include <stdlib.h>
    8.20  #include <stdint.h>
    8.21  #include <stdarg.h>
    8.22 @@ -234,4 +237,109 @@
    8.23  void environment_unset(struct environment *env);
    8.24  void environment_release(struct environment *env);
    8.25  
    8.26 +#ifdef MSWIN_API
    8.27 +char *razor_utf16_to_utf8(const wchar_t *utf16, int len);
    8.28 +wchar_t *razor_utf8_to_utf16(const char *utf8, int len);
    8.29 +#endif
    8.30 +
    8.31 +/* Atomic functions */
    8.32 +
    8.33 +#if HAVE_WINDOWS_KTM
    8.34 +struct razor_atomic {
    8.35 +	HANDLE transaction;
    8.36 +	int n_files;
    8.37 +	struct razor_atomic_file {
    8.38 +		wchar_t *path;
    8.39 +		HANDLE h;
    8.40 +	} *files;
    8.41 +	int in_undo;
    8.42 +	char *error_path;
    8.43 +	char *error_str;
    8.44 +	char *error_msg;
    8.45 +};
    8.46 +#elif ENABLE_ATOMIC
    8.47 +struct atomic_action {
    8.48 +	struct atomic_action *next;
    8.49 +	enum atomic_action_type {
    8.50 +		/* Complex actions */
    8.51 +		ACTION_MAKE_DIRS,
    8.52 +		ACTION_REMOVE,
    8.53 +		/* Primitive actions */
    8.54 +		ACTION_CREATE_DIR,
    8.55 +#if HAVE_SYMLINK
    8.56 +		ACTION_CREATE_SYMLINK,
    8.57 +#endif
    8.58 +		ACTION_MOVE,
    8.59 +	} type;
    8.60 +	struct {
    8.61 +		char *path;
    8.62 +		union atomic_action_args {
    8.63 +			struct {
    8.64 +				char *root;
    8.65 +			} make_dirs;
    8.66 +			struct {
    8.67 +				mode_t mode;
    8.68 +			} create_dir;
    8.69 +#if HAVE_SYMLINK
    8.70 +			struct {
    8.71 +				char *target;
    8.72 +			} create_symlink;
    8.73 +#endif
    8.74 +			struct {
    8.75 +				char *dest;
    8.76 +			} move;
    8.77 +		} u;
    8.78 +	} args;
    8.79 +};
    8.80 +
    8.81 +struct razor_atomic {
    8.82 +	struct atomic_action *actions;
    8.83 +	char *description;
    8.84 +	char *toplevel;
    8.85 +	unsigned next_file_tag;
    8.86 +	int in_undo;
    8.87 +	char *error_path;
    8.88 +	char *error_str;
    8.89 +	char *error_msg;
    8.90 +};
    8.91 +
    8.92 +char *atomic_action_attic_tmpnam(struct razor_atomic *atomic);
    8.93 +struct atomic_action *
    8.94 +atomic_action_list_prepend(struct atomic_action *list,
    8.95 +			   struct atomic_action *action);
    8.96 +struct atomic_action *atomic_action_new(enum atomic_action_type type);
    8.97 +void atomic_action_free(struct atomic_action *action);
    8.98 +struct atomic_action *atomic_action_list_reverse(struct atomic_action *list);
    8.99 +struct atomic_action *
   8.100 +atomic_action_do(struct razor_atomic *atomic, struct atomic_action *action);
   8.101 +void
   8.102 +atomic_action_undo(struct razor_atomic *atomic, struct atomic_action *action);
   8.103 +#else	/* !HAVE_WINDOWS_KTM && !ENABLE_ATOMIC */
   8.104 +struct razor_atomic {
   8.105 +	int in_undo;
   8.106 +	char *error_path;
   8.107 +	char *error_str;
   8.108 +	char *error_msg;
   8.109 +};
   8.110 +#endif
   8.111 +
   8.112 +int razor_allow_all_root_names(void);
   8.113 +int razor_valid_root_name(const char *name);
   8.114 +
   8.115 +#ifdef MSWIN_API
   8.116 +void
   8.117 +razor_atomic_set_error_mswin(struct razor_atomic *atomic, const wchar_t *path,
   8.118 +			     DWORD error);
   8.119 +#endif
   8.120 +
   8.121 +#if HAVE_WINDOWS_KTM
   8.122 +void
   8.123 +razor_atomic_set_error_str(struct razor_atomic *atomic, const wchar_t *path,
   8.124 +			   const char *str);
   8.125 +#else
   8.126 +void
   8.127 +razor_atomic_set_error_str(struct razor_atomic *atomic, const char *path,
   8.128 +			   const char *str);
   8.129 +#endif
   8.130 +
   8.131  #endif /* _RAZOR_INTERNAL_H_ */
     9.1 --- a/librazor/razor.h	Thu Feb 09 20:15:00 2012 +0000
     9.2 +++ b/librazor/razor.h	Thu Feb 09 20:42:08 2012 +0000
     9.3 @@ -104,8 +104,13 @@
     9.4   * that should either all succeed or all fail.
     9.5   *
     9.6   * Note that currently only Windows 7 has a native implementation and that
     9.7 - * the fallback implementation will not rollback even if an error does occur.
     9.8 - * This could (and should) be improved.
     9.9 + * while the emulated fallback implementation attempts to rollback the
    9.10 + * transaction if it fails, this is not guaranteed to succeed and that
    9.11 + * the transaction is held in core (and thus vulnernable to program crashes,
    9.12 + * power loss, etc.). This could (and should) be improved.
    9.13 + *
    9.14 + * Atomic transactions can be disabled via configure, in which case all
    9.15 + * bets are off.
    9.16   **/
    9.17  struct razor_atomic;
    9.18  
    9.19 @@ -331,7 +336,8 @@
    9.20  
    9.21  enum razor_install_action {
    9.22  	RAZOR_INSTALL_ACTION_ADD,
    9.23 -	RAZOR_INSTALL_ACTION_REMOVE
    9.24 +	RAZOR_INSTALL_ACTION_REMOVE,
    9.25 +	RAZOR_INSTALL_ACTION_COMMIT
    9.26  };
    9.27  
    9.28  struct razor_install_iterator *
    10.1 --- a/librazor/util.c	Thu Feb 09 20:15:00 2012 +0000
    10.2 +++ b/librazor/util.c	Thu Feb 09 20:42:08 2012 +0000
    10.3 @@ -349,3 +349,39 @@
    10.4  		return un.machine;
    10.5  #endif
    10.6  }
    10.7 +
    10.8 +#ifdef MSWIN_API
    10.9 +
   10.10 +char *razor_utf16_to_utf8(const wchar_t *utf16, int len)
   10.11 +{
   10.12 +	int n;
   10.13 +	char *utf8;
   10.14 +
   10.15 +	n = WideCharToMultiByte(CP_UTF8, 0, utf16, len, NULL, 0, NULL, NULL);
   10.16 +	if (len >= 0 && utf16[len])
   10.17 +		n++;
   10.18 +	utf8 = malloc(n);
   10.19 +	(void)WideCharToMultiByte(CP_UTF8, 0, utf16, len, utf8, n, NULL, NULL);
   10.20 +	if (len >= 0 && utf16[len])
   10.21 +		utf8[n - 1] = 0;
   10.22 +
   10.23 +	return utf8;
   10.24 +}
   10.25 +
   10.26 +wchar_t *razor_utf8_to_utf16(const char *utf8, int len)
   10.27 +{
   10.28 +	int n;
   10.29 +	wchar_t *utf16;
   10.30 +
   10.31 +	n = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0);
   10.32 +	if (len >= 0 && utf8[len])
   10.33 +		n++;
   10.34 +	utf16 = malloc(n * sizeof(wchar_t));
   10.35 +	(void)MultiByteToWideChar(CP_UTF8, 0, utf8, len, utf16, n);
   10.36 +	if (len >= 0 && utf8[len])
   10.37 +		utf16[n - 1] = 0;
   10.38 +
   10.39 +	return utf16;
   10.40 +}
   10.41 +
   10.42 +#endif	/* MSWIN_API */