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 */