From e766b5ace22a291d6451ec65de4dfcef013bb2d8 Mon Sep 17 00:00:00 2001 From: J. Ali Harlow Date: Thu, 9 Feb 2012 20:42:08 +0000 Subject: [PATCH] Emulate atomic transactions --- configure.ac | 11 +- librazor/Makefile.am | 4 + librazor/atomic-actions.c | 499 +++++++++++++++++++++++++ librazor/atomic-emulate.c | 249 +++++++++++++ librazor/atomic-ktm.c | 574 +++++++++++++++++++++++++++++ librazor/atomic-none.c | 249 +++++++++++++ librazor/atomic.c | 881 +++------------------------------------------ librazor/razor-internal.h | 110 ++++++- librazor/razor.h | 12 +- librazor/util.c | 36 ++ 10 files changed, 1783 insertions(+), 842 deletions(-) create mode 100644 librazor/atomic-actions.c create mode 100644 librazor/atomic-emulate.c create mode 100644 librazor/atomic-ktm.c create mode 100644 librazor/atomic-none.c diff --git a/configure.ac b/configure.ac index ff0c83a..88911de 100644 --- a/configure.ac +++ b/configure.ac @@ -33,6 +33,15 @@ AC_PROG_LN_S AC_SYS_LARGEFILE AM_PROG_CC_C_O +AC_ARG_ENABLE([atomic], + [AS_HELP_STRING([--disable-atomic], + [disable atomic transactions])], + [], + [enable_atomic=yes]) +if test "$enable_atomic" = "yes"; then + AC_DEFINE([ENABLE_ATOMIC],[1],[Define if atomic transactions are wanted.]) +fi + AC_MSG_CHECKING([for Microsoft Windows native API]) case $host_os in *mingw*) AC_DEFINE([MSWIN_API], 1, @@ -45,7 +54,7 @@ AC_MSG_RESULT([$mswin_api]) AM_CONDITIONAL(MSWIN_API, test "$mswin_api" = "yes") AC_SUBST(EXTRA_LIBS) -if test "$mswin_api" = "yes"; then +if test "$enable_atomic" = "yes" -a "$mswin_api" = "yes"; then AC_MSG_CHECKING([for Microsoft Windows Kernel Transaction Manager]) save_LIBS="$LIBS" LIBS="-lktmw32 $LIBS" diff --git a/librazor/Makefile.am b/librazor/Makefile.am index bc5c469..7196484 100644 --- a/librazor/Makefile.am +++ b/librazor/Makefile.am @@ -34,6 +34,10 @@ librazor_la_SOURCES = \ importer.c \ merger.c \ atomic.c \ + atomic-ktm.c \ + atomic-none.c \ + atomic-emulate.c \ + atomic-actions.c \ transaction.c if HAVE_LUA diff --git a/librazor/atomic-actions.c b/librazor/atomic-actions.c new file mode 100644 index 0000000..0b4662b --- /dev/null +++ b/librazor/atomic-actions.c @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2012 J. Ali Harlow + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#if ENABLE_ATOMIC && !HAVE_WINDOWS_KTM + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "razor-internal.h" + +char *atomic_action_attic_tmpnam(struct razor_atomic *atomic) +{ + char filename[17]; + sprintf(filename,"%03X",atomic->next_file_tag++); + return razor_concat(atomic->toplevel, "/", filename, NULL); +} + +static struct atomic_action * +atomic_action_list_pop_head(struct atomic_action **list) +{ + struct atomic_action *head; + + head = *list; + if (head) { + *list = head->next; + head->next = NULL; + } + + return head; +} + +struct atomic_action * +atomic_action_list_prepend(struct atomic_action *list, + struct atomic_action *action) +{ + assert(action->next == NULL); + + action->next=list; + + return action; +} + +static struct atomic_action * +atomic_action_list_concat(struct atomic_action *list1, + struct atomic_action *list2) +{ + struct atomic_action *action; + + if (!list1) + return list2; + + for(action=list1;action->next;action=action->next) + ; + + action->next=list2; + + return list1; +} + +void atomic_action_free(struct atomic_action *action) +{ + struct atomic_action *a; + + while(action) { + a = atomic_action_list_pop_head(&action); + + free(a->args.path); + + switch(a->type) { + case ACTION_MAKE_DIRS: + free(a->args.u.make_dirs.root); + break; + case ACTION_MOVE: + free(a->args.u.move.dest); + break; +#if HAVE_SYMLINK + case ACTION_CREATE_SYMLINK: + free(a->args.u.create_symlink.target); + break; +#endif + case ACTION_REMOVE: + case ACTION_CREATE_DIR: + break; + } + + free(a); + } +} + +struct atomic_action *atomic_action_new(enum atomic_action_type type) +{ + struct atomic_action *action; + + action = zalloc(sizeof *action); + action->type = type; + + return action; +} + +struct atomic_action *atomic_action_list_reverse(struct atomic_action *list) +{ + struct atomic_action *prev = NULL, *next; + + while(list) { + next = list->next; + list->next = prev; + prev = list; + list = next; + } + + return prev; +} + +/* + * All action_ functions take 1 action and return a list of primitive + * actions they took (such that the first action in the list is the + * last action they took). Primitive actions are always reversable. + * + * On failure, an error should be set on the atomic object (if it is + * not already set), the action freed and NULL returned. + * + * Whether they succeed or fail, they take ownership of the passed + * action and should thus either free it or return it back to their + * caller as appropriate. + * + * A NULL return means that nothing was done which may, or may not, + * indicate an error. + */ + +static struct atomic_action * +atomic_action_make_dirs(struct razor_atomic *atomic, + struct atomic_action *action) +{ + char buffer[PATH_MAX], *p; + const char *slash, *next; + struct atomic_action *primitives = NULL; + struct atomic_action *prim; + + if (razor_atomic_in_error_state(atomic)) { + atomic_action_free(action); + return NULL; + } + + strcpy(buffer, action->args.u.make_dirs.root); + p = buffer + strlen(buffer); + slash = action->args.path; + for (; *slash != '\0'; slash = next) { +#ifdef MSWIN_API + next = strpbrk(slash + 1, "/\\"); +#else + next = strchr(slash + 1, '/'); +#endif + if (next == NULL) + break; + + memcpy(p, slash, next - slash); + p += next - slash; + *p = '\0'; + + if (razor_valid_root_name(buffer)) + continue; + + prim = atomic_action_new(ACTION_CREATE_DIR); + prim->args.path = strdup(buffer); + prim->args.u.create_dir.mode = S_IRWXU | S_IRWXG | S_IRWXO; + primitives = atomic_action_list_prepend(primitives, prim); + } + primitives = atomic_action_list_reverse(primitives); + + return atomic_action_do(atomic, primitives); +} + +static struct atomic_action * +atomic_action_remove(struct razor_atomic *atomic, struct atomic_action *action) +{ +#ifdef MSWIN_API + wchar_t *path; + _WDIR *dir; +#else + DIR *dir; +#endif + struct atomic_action *prim; + + if (razor_atomic_in_error_state(atomic)) { + atomic_action_free(action); + return NULL; + } + + /* + * Non-empty directories should NOT be removed + */ +#ifdef MSWIN_API + path = razor_utf8_to_utf16(action->args.path, -1); + + dir = _wopendir(path); + if (dir && _wreaddir(dir)) { + atomic_action_free(action); + action = NULL; + } + _wclosedir(dir); +#else + dir = opendir(action->args.path); + if (dir && readdir(dir)) { + atomic_action_free(action); + action = NULL; + } + closedir(dir); +#endif + + if (action) { + prim = atomic_action_new(ACTION_MOVE); + prim->args.path = strdup(action->args.path); + prim->args.u.move.dest = atomic_action_attic_tmpnam(atomic); + + atomic_action_free(action); + } else + prim = NULL; + + return prim ? atomic_action_do(atomic, prim) : NULL; +} + +static struct atomic_action * +atomic_action_create_dir(struct razor_atomic *atomic, + struct atomic_action *action) +{ + mode_t mode; + + if (razor_atomic_in_error_state(atomic)) { + atomic_action_free(action); + return NULL; + } + + mode = action->args.u.create_dir.mode & (S_IRWXU | S_IRWXG | S_IRWXO); + + if (!mkdir(action->args.path, mode)) + return 0; + + if (errno != EEXIST || chmod(action->args.path, mode) < 0) { + if (!atomic->error_str) + razor_atomic_set_error_str(atomic, action->args.path, + strerror(errno)); + atomic_action_free(action); + return NULL; + } + + return action; +} + +static struct atomic_action *atomic_action_rmdir(struct razor_atomic *atomic, + struct atomic_action *action) +{ + if (razor_atomic_in_error_state(atomic)) { + atomic_action_free(action); + return NULL; + } + + if (rmdir(action->args.path) < 0) { + if (!atomic->error_str) + razor_atomic_set_error_str(atomic, action->args.path, + strerror(errno)); + atomic_action_free(action); + return NULL; + } else + return action; +} + +#if HAVE_SYMLINK +static struct atomic_action * +atomic_action_create_symlink(struct razor_atomic *atomic, + struct atomic_action *action) +{ + int r; + + if (razor_atomic_in_error_state(atomic)) { + atomic_action_free(action); + return NULL; + } + + r = symlink(action->args.u.create_symlink.target, action->args.path); + if (r < 0) { + if (!atomic->error_str) + razor_atomic_set_error_str(atomic, NULL, + strerror(errno)); + atomic_action_free(action); + return NULL; + } + + return action; +} + +static struct atomic_action * +atomic_action_remove_symlink(struct razor_atomic *atomic, + struct atomic_action *action) +{ + if (razor_atomic_in_error_state(atomic)) { + atomic_action_free(action); + return NULL; + } + + if (unlink(action->args.path) < 0) { + if (!atomic->error_str) + razor_atomic_set_error_str(atomic, NULL, + strerror(errno)); + atomic_action_free(action); + return NULL; + } + + return action; +} +#endif + +static int +move_file(struct razor_atomic *atomic, const char *path, const char *dest) +{ +#ifdef MSWIN_API + wchar_t *oldbuf, *newbuf; + const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING; + + newbuf = razor_utf8_to_utf16(dest, -1); + oldbuf = razor_utf8_to_utf16(path, -1); + + /* + * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will + * cover every case we care about _except_ replacing an empty + * directory with a file. Calling RemoveDirectory() will deal + * with this case while having no effect in all other cases. + */ + (void)RemoveDirectoryW(newbuf); + + if (!MoveFileExW(oldbuf, newbuf, flags)) { + if (!atomic->error_str) + razor_atomic_set_error_mswin(atomic, newbuf, + GetLastError()); + return -1; + } + + free(newbuf); + free(oldbuf); +#else + if (rename(path, dest)) { + if (!atomic->error_str) + razor_atomic_set_error_str(atomic, dest, + strerror(errno)); + return -1; + } +#endif + + return 0; +} + +static struct atomic_action * +atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action) +{ + if (razor_atomic_in_error_state(atomic)) { + atomic_action_free(action); + return NULL; + } + + if (move_file(atomic, action->args.path, action->args.u.move.dest)) { + atomic_action_free(action); + return NULL; + } + + return action; +} + +static struct atomic_action * +atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action) +{ + if (razor_atomic_in_error_state(atomic)) { + atomic_action_free(action); + return NULL; + } + + if (move_file(atomic, action->args.u.move.dest, action->args.path)) { + atomic_action_free(action); + return NULL; + } + + return action; +} + +static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic, + struct atomic_action *action) +{ + switch(action->type) { + case ACTION_MAKE_DIRS: + action = atomic_action_make_dirs(atomic, action); + break; + case ACTION_REMOVE: + action = atomic_action_remove(atomic, action); + break; + case ACTION_CREATE_DIR: + action = atomic_action_create_dir(atomic, action); + break; + case ACTION_MOVE: + action = atomic_action_move(atomic, action); + break; +#if HAVE_SYMLINK + case ACTION_CREATE_SYMLINK: + action = atomic_action_create_symlink(atomic, action); + break; +#endif + } + return action; +} + +static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic, + struct atomic_action *action) +{ + switch(action->type) { + case ACTION_MAKE_DIRS: + case ACTION_REMOVE: + /* Complex actions: should never happen */ + break; + case ACTION_CREATE_DIR: + action = atomic_action_rmdir(atomic, action); + break; + case ACTION_MOVE: + action = atomic_action_unmove(atomic, action); + break; +#if HAVE_SYMLINK + case ACTION_CREATE_SYMLINK: + action = atomic_action_remove_symlink(atomic, action); + break; +#endif + } + return action; +} + +/* + * Note that undo has no error checking. + */ + +void atomic_action_undo(struct razor_atomic *atomic, + struct atomic_action *actions) +{ + struct atomic_action *a; + + atomic->in_undo = 1; + + while (actions) { + a = atomic_action_list_pop_head(&actions); + a = atomic_action_reverse(atomic, a); + atomic_action_free(a); + } + + atomic->in_undo = 0; +} + +struct atomic_action *atomic_action_do(struct razor_atomic *atomic, + struct atomic_action *actions) +{ + struct atomic_action *done = NULL, *a; + + if (razor_atomic_in_error_state(atomic)) { + atomic_action_free(actions); + return NULL; + } + + while (actions) { + a = atomic_action_list_pop_head(&actions); + a = atomic_action_apply(atomic, a); + if (a) + done = atomic_action_list_concat(a, done); + if (razor_atomic_in_error_state(atomic)) { + atomic_action_undo(atomic, done); + done = NULL; + atomic_action_free(actions); + } + } + + return done; +} + +#endif /* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */ diff --git a/librazor/atomic-emulate.c b/librazor/atomic-emulate.c new file mode 100644 index 0000000..c8f88e0 --- /dev/null +++ b/librazor/atomic-emulate.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2012 J. Ali Harlow + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#if ENABLE_ATOMIC && !HAVE_WINDOWS_KTM + +#include +#include +#include +#include +#include +#include +#include +#include +#include "razor-internal.h" + +/* + * Emulated atomic support + * + * This implementation is better than nothing, but is certainly not atomic. + * It does have a couple of advantages over atomic-none: + * - If a file operation fails while a package is being installed we + * have a good chance of being able to rollback the transaction to + * a well-known state. + * - We behave similarly to atomic-ktm in that changes are not visible + * on disk to non-atomic operations (eg., scripts) until the atomic + * is committed. This makes the testsuite more likely to pick up + * problems that would otherwise only be found when using razor on + * an MS-Windows system which supports KTM. + */ + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +static void recursive_remove(const char *directory) +{ + DIR *dp; + struct dirent *dirp; + char *buf; + + dp = opendir(directory); + while((dirp = readdir(dp))) { + if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) { + buf = malloc(strlen(directory) + strlen(dirp->d_name) + + 2); + sprintf(buf, "%s/%s", directory, dirp->d_name); + if (remove(buf) < 0) + recursive_remove(buf); + free(buf); + } + } + + rmdir(directory); +} + +RAZOR_EXPORT struct razor_atomic *razor_atomic_open(const char *description) +{ + struct razor_atomic *atomic; + + atomic = zalloc(sizeof *atomic); + + atomic->toplevel = strdup(".atomic-XXXXXX"); + if (!mkdtemp(atomic->toplevel)) { + free(atomic->toplevel); + free(atomic); + return NULL; + } + + atomic->description = strdup(description); + + return atomic; +} + +RAZOR_EXPORT int razor_atomic_commit(struct razor_atomic *atomic) +{ + struct atomic_action *actions; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + if (atomic->actions) { + actions = atomic_action_list_reverse(atomic->actions); + atomic->actions = NULL; + actions = atomic_action_do(atomic, actions); + atomic_action_free(actions); + } + + if (atomic->toplevel) { + recursive_remove(atomic->toplevel); + free(atomic->toplevel); + atomic->toplevel = NULL; + } + + return !!atomic->error_str; +} + +RAZOR_EXPORT void razor_atomic_destroy(struct razor_atomic *atomic) +{ + if (atomic->toplevel) { + recursive_remove(atomic->toplevel); + free(atomic->toplevel); + atomic->toplevel = NULL; + } + + free(atomic->error_path); + free(atomic->error_str); + free(atomic->error_msg); + free(atomic); +} + +RAZOR_EXPORT int +razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root, + const char *path) +{ + struct atomic_action *a; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + a = atomic_action_new(ACTION_MAKE_DIRS); + a->args.path = strdup(path); + a->args.u.make_dirs.root = strdup(root); + atomic->actions = atomic_action_list_prepend(atomic->actions, a); + + return 0; +} + +RAZOR_EXPORT int +razor_atomic_remove(struct razor_atomic *atomic, const char *path) +{ + struct atomic_action *a; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + a = atomic_action_new(ACTION_REMOVE); + a->args.path = strdup(path); + atomic->actions = atomic_action_list_prepend(atomic->actions, a); + + return 0; +} + +RAZOR_EXPORT int +razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath, + const char *newpath) +{ + struct atomic_action *a; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + a = atomic_action_new(ACTION_MOVE); + a->args.path = strdup(oldpath); + a->args.u.move.dest = strdup(newpath); + atomic->actions = atomic_action_list_prepend(atomic->actions, a); + + return 0; +} + +RAZOR_EXPORT int +razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname, + mode_t mode) +{ + struct atomic_action *a; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + a = atomic_action_new(ACTION_CREATE_DIR); + a->args.path = strdup(dirname); + a->args.u.create_dir.mode = mode; + atomic->actions = atomic_action_list_prepend(atomic->actions, a); + + return 0; +} + +RAZOR_EXPORT int +razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target, + const char *path) +{ +#if HAVE_SYMLINK + struct atomic_action *a; +#endif + + if (razor_atomic_in_error_state(atomic)) + return -1; + +#if HAVE_SYMLINK + a = atomic_action_new(ACTION_CREATE_SYMLINK); + a->args.path = strdup(path); + a->args.u.create_symlink.target = strdup(target); + atomic->actions = atomic_action_list_prepend(atomic->actions, a); + + return 0; +#else + razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported " + "on this platform"); + + return -1; +#endif +} + +RAZOR_EXPORT int +razor_atomic_create_file(struct razor_atomic *atomic, const char *filename, + mode_t mode) +{ + int fd; + struct atomic_action *a; + char *tmpnam; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + atomic->error_path = strdup(filename); + tmpnam = atomic_action_attic_tmpnam(atomic); + fd = open(tmpnam, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + + if (fd == -1) + razor_atomic_set_error_str(atomic, NULL, strerror(errno)); + else { + a = atomic_action_new(ACTION_MOVE); + a->args.path = tmpnam; + a->args.u.move.dest = strdup(filename); + atomic->actions = atomic_action_list_prepend(atomic->actions, + a); + } + + return fd; +} + +#endif /* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */ diff --git a/librazor/atomic-ktm.c b/librazor/atomic-ktm.c new file mode 100644 index 0000000..5b4b751 --- /dev/null +++ b/librazor/atomic-ktm.c @@ -0,0 +1,574 @@ +/* + * Copyright (C) 2011-2012 J. Ali Harlow + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#if HAVE_WINDOWS_KTM + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "razor.h" +#include "razor-internal.h" + +#define RAZOR_ASCII_ISALPHA(c) \ + ((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z') + +static int +razor_valid_root_name2(const wchar_t *name) +{ + if (razor_allow_all_root_names()) + return !wcschr(name, '/'); + + return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' && + name[2] == '\0'; +} + +struct razor_wstr { + wchar_t *str; + int len, allocated; +}; + +static struct razor_wstr * +razor_wstr_create(const char *init, int len) +{ + int n; + struct razor_wstr *wstr; + + wstr = malloc(sizeof(struct razor_wstr)); + + n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0); + if (len >= 0 && init[len]) + wstr->len = n++; + else + wstr->len = n - 1; + + wstr->allocated = n * 2; + wstr->str = malloc(wstr->allocated * sizeof(wchar_t)); + if (!wstr->str) { + free(wstr); + return NULL; + } + + (void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n); + if (len >= 0 && init[len]) + wstr->str[wstr->len] = 0; + + return wstr; +} + +static int +razor_wstr_append(struct razor_wstr *wstr, const char *s, int len) +{ + int n, allocated; + wchar_t *str; + + n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0); + if (len < 0 || !s[len]) + n--; + + if (wstr->allocated <= wstr->len + n) { + allocated = (wstr->len + n + 1) * 2; + str = realloc(wstr->str, allocated * sizeof(wchar_t)); + if (!str) + return -1; + wstr->allocated = allocated; + wstr->str = str; + } + + (void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n); + wstr->len += n; + wstr->str[wstr->len] = 0; + + return 0; +} + +static void +razor_wstr_destroy(struct razor_wstr *wstr) +{ + free(wstr->str); + free(wstr); +} + +RAZOR_EXPORT struct razor_atomic * +razor_atomic_open(const char *description) +{ + wchar_t *buf; + struct razor_atomic *atomic; + + atomic = zalloc(sizeof *atomic); + buf = razor_utf8_to_utf16(description, -1); + atomic->transaction = CreateTransaction(NULL, 0, + TRANSACTION_DO_NOT_PROMOTE, + 0, 0, 0, buf); + free(buf); + + return atomic; +} + +void +razor_atomic_set_error_str(struct razor_atomic *atomic, const wchar_t *path, + const char *str) +{ + assert(!atomic->error_str); + + free(atomic->error_path); + + if (path) + atomic->error_path = razor_utf16_to_utf8(path, -1); + else + atomic->error_path = NULL; + + atomic->error_str = strdup(str); +} + +static void +razor_atomic_set_error(struct razor_atomic *atomic, const wchar_t *path, + DWORD error) +{ + wchar_t *buf; + + assert(!atomic->error_str); + + free(atomic->error_path); + + if (path) + atomic->error_path = razor_utf16_to_utf8(path, -1); + else + atomic->error_path = NULL; + + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER| + FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), + (LPWSTR)&buf, 0, NULL); + atomic->error_str = razor_utf16_to_utf8(buf, -1); + LocalFree(buf); +} + +RAZOR_EXPORT int +razor_atomic_commit(struct razor_atomic *atomic) +{ + int retval; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + retval = !CommitTransaction(atomic->transaction); + + if (retval) { + razor_atomic_set_error(atomic, NULL, GetLastError()); + RollbackTransaction(atomic->transaction); + } + + CloseHandle(atomic->transaction); + atomic->transaction = INVALID_HANDLE_VALUE; + + return retval; +} + +RAZOR_EXPORT void +razor_atomic_destroy(struct razor_atomic *atomic) +{ + int i; + + for(i = 0; i < atomic->n_files; i++) { + if (atomic->files[i].h != INVALID_HANDLE_VALUE) { + CloseHandle(atomic->files[i].h); + free(atomic->files[i].path); + } + } + free(atomic->files); + if (atomic->transaction != INVALID_HANDLE_VALUE) { + RollbackTransaction(atomic->transaction); + CloseHandle(atomic->transaction); + } + free(atomic->error_path); + free(atomic->error_str); + free(atomic->error_msg); + free(atomic); +} + +RAZOR_EXPORT int +razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root, + const char *path) +{ + struct razor_wstr *buffer; + const char *slash, *next; + WIN32_FILE_ATTRIBUTE_DATA fa; + DWORD err; + int r, creating = 0; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + buffer = razor_wstr_create(root, -1); + slash = path; + + for (; *slash != '\0'; slash = next) { + next = strpbrk(slash + 1, "/\\"); + if (next == NULL) + break; + + razor_wstr_append(buffer, slash, next - slash); + + if (!creating) { + if (razor_valid_root_name2(buffer->str)) + continue; + + r = GetFileAttributesTransactedW(buffer->str, + GetFileExInfoStandard, + &fa, + atomic->transaction); + + if (!r) { + err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND) { + creating = 1; + } else { + razor_atomic_set_error(atomic, + buffer->str, + err); + razor_wstr_destroy(buffer); + return -1; + } + } else if (!(fa.dwFileAttributes& + FILE_ATTRIBUTE_DIRECTORY)) { + razor_atomic_set_error_str(atomic, buffer->str, + "Not a directory"); + razor_wstr_destroy(buffer); + return -1; + } + } + if (creating) { + if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL, + atomic->transaction)) { + razor_atomic_set_error(atomic, buffer->str, + GetLastError()); + razor_wstr_destroy(buffer); + return -1; + } + + /* FIXME: What to do about permissions for dirs we + * have to create but are not in the cpio archive? */ + } + } + + razor_wstr_destroy(buffer); + + return 0; +} + +RAZOR_EXPORT int +razor_atomic_remove(struct razor_atomic *atomic, const char *path) +{ + wchar_t *buf; + DWORD err; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + buf = razor_utf8_to_utf16(path, -1); + + if (DeleteFileTransactedW(buf, atomic->transaction)) { + free(buf); + return 0; + } + + err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { + free(buf); + return 0; + } + + if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL, + atomic->transaction)) { + if (DeleteFileTransactedW(buf, atomic->transaction)) { + free(buf); + return 0; + } + err = GetLastError(); + } + + if (RemoveDirectoryTransactedW(buf, atomic->transaction) || + GetLastError() == ERROR_DIR_NOT_EMPTY) { + free(buf); + return 0; + } + + /* + * It would be tempting to use: + * MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) + * but unless we can guarantee that the system will be rebooted + * before we (or some other application) write another file with the + * same path, this is likely to cause more problems than it solves. + */ + + razor_atomic_set_error(atomic, buf, err); + free(buf); + return -1; +} + +RAZOR_EXPORT int +razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath, + const char *newpath) +{ + wchar_t *oldbuf, *newbuf; + const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + newbuf = razor_utf8_to_utf16(newpath, -1); + oldbuf = razor_utf8_to_utf16(oldpath, -1); + + /* + * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will + * cover every case we care about _except_ replacing an empty + * directory with a file. Calling RemoveDirectoryTransacted() will deal + * with this case while having no effect in all other cases. + */ + (void)RemoveDirectoryTransactedW(newbuf, atomic->transaction); + + if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags, + atomic->transaction)) + razor_atomic_set_error(atomic, newbuf, GetLastError()); + + free(newbuf); + free(oldbuf); + + return !!atomic->error_str; +} + +RAZOR_EXPORT int +razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname, + mode_t mode) +{ + wchar_t *buf; + DWORD err; + WIN32_FILE_ATTRIBUTE_DATA fa; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + buf = razor_utf8_to_utf16(dirname, -1); + + if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) { + err = GetLastError(); + if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) { +abort: + razor_atomic_set_error(atomic, buf, err); + free(buf); + return -1; + } + + if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard, + &fa, atomic->transaction)) + goto abort; + + if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) { + if (razor_atomic_remove(atomic, dirname)) { + free(buf); + return -1; + } + if (!CreateDirectoryTransactedW(NULL, buf, NULL, + atomic->transaction)) { + err = GetLastError(); + goto abort; + } + } + } + + free(buf); + + return 0; +} + +RAZOR_EXPORT int +razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target, + const char *path) +{ + if (razor_atomic_in_error_state(atomic)) + return -1; + + /* + * This isn't true, but symbolic links under Windows 7 + * need to know whether the target is a directory or not + * and we don't always know that at the time when the + * link is created, so it's a convienent lie for now. + */ + razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported " + "on this platform"); + + return -1; +} + +RAZOR_EXPORT int +razor_atomic_create_file(struct razor_atomic *atomic, const char *filename, + mode_t mode) +{ + DWORD attribs; + struct razor_atomic_file *files; + int i = atomic->n_files; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + files = realloc(atomic->files, + (atomic->n_files+1) * sizeof(struct razor_atomic_file)); + if (!files) { + razor_atomic_set_error_str(atomic, NULL, "Not enough memory"); + return -1; + } + atomic->n_files++; + atomic->files = files; + + files[i].path = razor_utf8_to_utf16(filename, -1); + + /* + * Passing CREATE_ALWAYS to CreateFileTransacted() will cover + * every case we care about _except_ replacing an empty directory + * with a file. Calling RemoveDirectoryTransacted() will deal + * with this case while having no effect in all other cases. + */ + (void)RemoveDirectoryTransactedW(files[i].path, atomic->transaction); + + if (mode & S_IWUSR) + attribs = FILE_ATTRIBUTE_NORMAL; + else + attribs = FILE_ATTRIBUTE_READONLY; + + files[i].h = CreateFileTransactedW(files[i].path, GENERIC_WRITE, + 0, NULL, CREATE_ALWAYS, attribs, + NULL, atomic->transaction, NULL, + NULL); + + if (files[i].h == INVALID_HANDLE_VALUE) { + razor_atomic_set_error(atomic, files[i].path, GetLastError()); + free(files[i].path); + atomic->n_files--; + return -1; + } + + return i; +} + +RAZOR_EXPORT int +razor_atomic_write(struct razor_atomic *atomic, int handle, const void *data, + size_t size) +{ + DWORD written; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + assert(handle < atomic->n_files); + assert(atomic->files[handle].h != INVALID_HANDLE_VALUE); + + while(size) { + if (!WriteFile(atomic->files[handle].h, data, size, &written, + NULL)) { + razor_atomic_set_error(atomic, + atomic->files[handle].path, + GetLastError()); + + (void)CloseHandle(atomic->files[handle].h); + free(atomic->files[handle].path); + atomic->files[handle].path = NULL; + atomic->files[handle].h = INVALID_HANDLE_VALUE; + + return -1; + } + + data += written; + size -= written; + } + + return 0; +} + +RAZOR_EXPORT int +razor_atomic_sync(struct razor_atomic *atomic, int handle) +{ + HANDLE h; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + assert(handle < atomic->n_files); + assert(atomic->files[handle].h != INVALID_HANDLE_VALUE); + + if (!CloseHandle(atomic->files[handle].h)) { + razor_atomic_set_error(atomic, atomic->files[handle].path, + GetLastError()); + free(atomic->files[handle].path); + atomic->files[handle].path = NULL; + atomic->files[handle].h = INVALID_HANDLE_VALUE; + return -1; + } + + h = CreateFileTransactedW(atomic->files[handle].path, GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, 0, NULL, + atomic->transaction, NULL, NULL); + atomic->files[handle].h = h; + + if (atomic->files[handle].h == INVALID_HANDLE_VALUE) { + razor_atomic_set_error(atomic, atomic->files[handle].path, + GetLastError()); + free(atomic->files[handle].path); + atomic->files[handle].path = NULL; + return -1; + } + + return !!atomic->error_str; +} + +RAZOR_EXPORT int +razor_atomic_close(struct razor_atomic *atomic, int handle) +{ + if (razor_atomic_in_error_state(atomic)) + return -1; + + assert(handle < atomic->n_files); + assert(atomic->files[handle].h != INVALID_HANDLE_VALUE); + + if (!CloseHandle(atomic->files[handle].h)) + razor_atomic_set_error(atomic, atomic->files[handle].path, + GetLastError()); + + free(atomic->files[handle].path); + atomic->files[handle].path = NULL; + atomic->files[handle].h = INVALID_HANDLE_VALUE; + + while(atomic->n_files > 0 && + atomic->files[atomic->n_files-1].h == INVALID_HANDLE_VALUE) + atomic->n_files--; + + return !!atomic->error_str; +} + +#endif /* HAVE_WINDOWS_KTM */ diff --git a/librazor/atomic-none.c b/librazor/atomic-none.c new file mode 100644 index 0000000..75b7434 --- /dev/null +++ b/librazor/atomic-none.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2011-2012 J. Ali Harlow + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#if !ENABLE_ATOMIC + +#include +#ifdef MSWIN_API +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "razor.h" +#include "razor-internal.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +RAZOR_EXPORT struct razor_atomic * +razor_atomic_open(const char *description) +{ + struct razor_atomic *atomic; + + atomic = zalloc(sizeof *atomic); + + return atomic; +} + +RAZOR_EXPORT int +razor_atomic_commit(struct razor_atomic *atomic) +{ + return razor_atomic_in_error_state(atomic); +} + +RAZOR_EXPORT void +razor_atomic_destroy(struct razor_atomic *atomic) +{ + free(atomic->error_path); + free(atomic->error_str); + free(atomic->error_msg); + free(atomic); +} + +RAZOR_EXPORT int +razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root, + const char *path) +{ + char buffer[PATH_MAX], *p; + const char *slash, *next; + struct stat buf; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + strcpy(buffer, root); + p = buffer + strlen(buffer); + slash = path; + for (slash = path; *slash != '\0'; slash = next) { +#ifdef MSWIN_API + next = strpbrk(slash + 1, "/\\"); +#else + next = strchr(slash + 1, '/'); +#endif + if (next == NULL) + break; + + memcpy(p, slash, next - slash); + p += next - slash; + *p = '\0'; + + if (razor_valid_root_name(buffer)) + continue; + + if (stat(buffer, &buf) == 0) { + if (!S_ISDIR(buf.st_mode)) { + razor_atomic_set_error_str(atomic, buffer, + "Not a directory"); + return -1; + } + } else if (mkdir(buffer, 0777) < 0) { + razor_atomic_set_error_str(atomic, buffer, + strerror(errno)); + return -1; + } + } + + return 0; +} + +RAZOR_EXPORT int +razor_atomic_remove(struct razor_atomic *atomic, const char *path) +{ +#ifdef MSWIN_API + wchar_t *buf; + DWORD err; +#endif + + if (razor_atomic_in_error_state(atomic)) + return -1; + +#ifdef MSWIN_API + buf = razor_utf8_to_utf16(path, -1); + + if (!DeleteFileW(buf)) { + err = GetLastError(); + if (err != ERROR_FILE_NOT_FOUND && + err != ERROR_PATH_NOT_FOUND && + !(SetFileAttributesW(buf, FILE_ATTRIBUTE_NORMAL) && + DeleteFileW(buf)) && + !RemoveDirectoryW(buf) && + GetLastError() != ERROR_DIR_NOT_EMPTY) + razor_atomic_set_error_mswin(atomic, buf, err); + } + + free(buf); +#else + if (remove(path)) + razor_atomic_set_error_str(atomic, path, strerror(errno)); +#endif + + return !!atomic->error_str; +} + +RAZOR_EXPORT int +razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath, + const char *newpath) +{ +#ifdef MSWIN_API + wchar_t *oldbuf, *newbuf; + const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING; +#endif + + if (razor_atomic_in_error_state(atomic)) + return -1; + +#ifdef MSWIN_API + newbuf = razor_utf8_to_utf16(newpath, -1); + oldbuf = razor_utf8_to_utf16(oldpath, -1); + + /* + * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will + * cover every case we care about _except_ replacing an empty + * directory with a file. Calling RemoveDirectory() will deal + * with this case while having no effect in all other cases. + */ + (void)RemoveDirectoryW(newbuf); + + if (!MoveFileExW(oldbuf, newbuf, flags)) + razor_atomic_set_error_mswin(atomic, newbuf, GetLastError()); + + free(newbuf); + free(oldbuf); +#else + if (rename(oldpath, newpath)) + razor_atomic_set_error_str(atomic, newpath, strerror(errno)); +#endif + + return !!atomic->error_str; +} + +RAZOR_EXPORT int +razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname, + mode_t mode) +{ + if (razor_atomic_in_error_state(atomic)) + return -1; + + if (!mkdir(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO))) + return 0; + + if (errno != EEXIST) { + razor_atomic_set_error_str(atomic, dirname, strerror(errno)); + return -1; + } + + if (chmod(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)) < 0) { + razor_atomic_set_error_str(atomic, dirname, strerror(errno)); + return -1; + } + + return 0; +} + +RAZOR_EXPORT int +razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target, + const char *path) +{ + if (razor_atomic_in_error_state(atomic)) + return -1; + +#if HAVE_SYMLINK + if (symlink(target, path) < 0) { + razor_atomic_set_error_str(atomic, NULL, strerror(errno)); + return -1; + } + + return 0; +#else + razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported " + "on this platform"); + + return -1; +#endif +} + +RAZOR_EXPORT int +razor_atomic_create_file(struct razor_atomic *atomic, const char *filename, + mode_t mode) +{ + int fd; + + if (razor_atomic_in_error_state(atomic)) + return -1; + + atomic->error_path = strdup(filename); + fd = open(atomic->error_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + + if (fd == -1) + razor_atomic_set_error_str(atomic, NULL, strerror(errno)); + + return fd; +} + +#endif /* !ENABLE_ATOMIC */ diff --git a/librazor/atomic.c b/librazor/atomic.c index ffc5bbf..261afe7 100644 --- a/librazor/atomic.c +++ b/librazor/atomic.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 J. Ali Harlow + * Copyright (C) 2011-2012 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,21 +19,15 @@ #include "config.h" #include -#ifdef MSWIN_API -#include -#endif #include #include #include #include -#include +#include #include +#include #include #include -#if HAVE_WINDOWS_KTM -#include -#include -#endif #include "razor.h" #include "razor-internal.h" @@ -42,13 +36,6 @@ * Atomic transactions */ -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -#define RAZOR_ASCII_ISALPHA(c) \ - ((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z') - static int allow_all_root_names = 0; /* @@ -60,596 +47,54 @@ razor_disable_root_name_checks(int disable) allow_all_root_names = disable; } -#ifdef MSWIN_API - -static char * -razor_utf16_to_utf8(const wchar_t *utf16, int len) -{ - int n; - char *utf8; - - n = WideCharToMultiByte(CP_UTF8, 0, utf16, len, NULL, 0, NULL, NULL); - if (len >= 0 && utf16[len]) - n++; - utf8 = malloc(n); - (void)WideCharToMultiByte(CP_UTF8, 0, utf16, len, utf8, n, NULL, NULL); - if (len >= 0 && utf16[len]) - utf8[n - 1] = 0; - - return utf8; -} - -static wchar_t * -razor_utf8_to_utf16(const char *utf8, int len) -{ - int n; - wchar_t *utf16; - - n = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0); - if (len >= 0 && utf8[len]) - n++; - utf16 = malloc(n * sizeof(wchar_t)); - (void)MultiByteToWideChar(CP_UTF8, 0, utf8, len, utf16, n); - if (len >= 0 && utf8[len]) - utf16[n - 1] = 0; - - return utf16; -} - -#endif /* MSWIN_API */ - -#if HAVE_WINDOWS_KTM - -static int -razor_valid_root_name(const wchar_t *name) -{ - if (allow_all_root_names) - return !wcschr(name, '/'); - - return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' && - name[2] == '\0'; -} - -struct razor_atomic { - HANDLE transaction; - int n_files; - struct razor_atomic_file { - wchar_t *path; - HANDLE h; - } *files; - char *error_path; - char *error_str; - char *error_msg; -}; - -struct razor_wstr { - wchar_t *str; - int len, allocated; -}; - -static struct razor_wstr * -razor_wstr_create(const char *init, int len) -{ - int n; - struct razor_wstr *wstr; - - wstr = malloc(sizeof(struct razor_wstr)); - - n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0); - if (len >= 0 && init[len]) - wstr->len = n++; - else - wstr->len = n - 1; - - wstr->allocated = n * 2; - wstr->str = malloc(wstr->allocated * sizeof(wchar_t)); - if (!wstr->str) { - free(wstr); - return NULL; - } - - (void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n); - if (len >= 0 && init[len]) - wstr->str[wstr->len] = 0; - - return wstr; -} - -static int -razor_wstr_append(struct razor_wstr *wstr, const char *s, int len) -{ - int n, allocated; - wchar_t *str; - - n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0); - if (len < 0 || !s[len]) - n--; - - if (wstr->allocated <= wstr->len + n) { - allocated = (wstr->len + n + 1) * 2; - str = realloc(wstr->str, allocated * sizeof(wchar_t)); - if (!str) - return -1; - wstr->allocated = allocated; - wstr->str = str; - } - - (void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n); - wstr->len += n; - wstr->str[wstr->len] = 0; - - return 0; -} - -static void -razor_wstr_destroy(struct razor_wstr *wstr) -{ - free(wstr->str); - free(wstr); -} - -RAZOR_EXPORT struct razor_atomic * -razor_atomic_open(const char *description) -{ - wchar_t *buf; - struct razor_atomic *atomic; - - atomic = zalloc(sizeof *atomic); - buf = razor_utf8_to_utf16(description, -1); - atomic->transaction = CreateTransaction(NULL, 0, - TRANSACTION_DO_NOT_PROMOTE, - 0, 0, 0, buf); - free(buf); - - return atomic; -} - -static void -razor_atomic_set_error_str(struct razor_atomic *atomic, const wchar_t *path, - const char *str) +int +razor_allow_all_root_names(void) { - assert(!atomic->error_str); - - free(atomic->error_path); - - if (path) - atomic->error_path = razor_utf16_to_utf8(path, -1); - else - atomic->error_path = NULL; - - atomic->error_str = strdup(str); + return allow_all_root_names; } -static void -razor_atomic_set_error(struct razor_atomic *atomic, const wchar_t *path, - DWORD error) -{ - wchar_t *buf; - - assert(!atomic->error_str); - - free(atomic->error_path); - - if (path) - atomic->error_path = razor_utf16_to_utf8(path, -1); - else - atomic->error_path = NULL; - - FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER| - FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), - (LPWSTR)&buf, 0, NULL); - atomic->error_str = razor_utf16_to_utf8(buf, -1); - LocalFree(buf); -} - -RAZOR_EXPORT int -razor_atomic_commit(struct razor_atomic *atomic) +RAZOR_EXPORT const char * +razor_atomic_get_error_msg(struct razor_atomic *atomic) { - int retval; - - if (atomic->error_str) - return -1; - - retval = !CommitTransaction(atomic->transaction); - - if (retval) { - razor_atomic_set_error(atomic, NULL, GetLastError()); - RollbackTransaction(atomic->transaction); + if (!atomic->error_msg) { + if (atomic->error_path) + atomic->error_msg = razor_concat(atomic->error_path, + ": ", + atomic->error_str, + NULL); + else + atomic->error_msg = strdup(atomic->error_str); } - CloseHandle(atomic->transaction); - atomic->transaction = INVALID_HANDLE_VALUE; - - return retval; + return atomic->error_msg; } RAZOR_EXPORT void -razor_atomic_destroy(struct razor_atomic *atomic) -{ - int i; - - for(i = 0; i < atomic->n_files; i++) { - if (atomic->files[i].h != INVALID_HANDLE_VALUE) { - CloseHandle(atomic->files[i].h); - free(atomic->files[i].path); - } - } - free(atomic->files); - if (atomic->transaction != INVALID_HANDLE_VALUE) { - RollbackTransaction(atomic->transaction); - CloseHandle(atomic->transaction); - } - free(atomic->error_path); - free(atomic->error_str); - free(atomic->error_msg); - free(atomic); -} - -RAZOR_EXPORT int -razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root, - const char *path) -{ - struct razor_wstr *buffer; - const char *slash, *s, *next; - WIN32_FILE_ATTRIBUTE_DATA fa; - DWORD err; - int r, creating = 0; - - if (atomic->error_str) - return -1; - - buffer = razor_wstr_create(root, -1); - slash = path; - - for (; *slash != '\0'; slash = next) { - next = strpbrk(slash + 1, "/\\"); - if (next == NULL) - break; - - razor_wstr_append(buffer, slash, next - slash); - - if (!creating) { - if (razor_valid_root_name(buffer->str)) - continue; - - r = GetFileAttributesTransactedW(buffer->str, - GetFileExInfoStandard, - &fa, - atomic->transaction); - - if (!r) { - err = GetLastError(); - if (err == ERROR_FILE_NOT_FOUND) { - creating = 1; - } else { - razor_atomic_set_error(atomic, - buffer->str, - err); - razor_wstr_destroy(buffer); - return -1; - } - } else if (!(fa.dwFileAttributes& - FILE_ATTRIBUTE_DIRECTORY)) { - razor_atomic_set_error_str(atomic, buffer->str, - "Not a directory"); - razor_wstr_destroy(buffer); - return -1; - } - } - if (creating) { - if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL, - atomic->transaction)) { - razor_atomic_set_error(atomic, buffer->str, - GetLastError()); - razor_wstr_destroy(buffer); - return -1; - } - - /* FIXME: What to do about permissions for dirs we - * have to create but are not in the cpio archive? */ - } - } - - razor_wstr_destroy(buffer); - - return 0; -} - -RAZOR_EXPORT int -razor_atomic_remove(struct razor_atomic *atomic, const char *path) -{ - wchar_t *buf; - DWORD err; - - if (atomic->error_str) - return -1; - - buf = razor_utf8_to_utf16(path, -1); - - if (DeleteFileTransactedW(buf, atomic->transaction)) { - free(buf); - return 0; - } - - err = GetLastError(); - if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) { - free(buf); - return 0; - } - - if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL, - atomic->transaction)) { - if (DeleteFileTransactedW(buf, atomic->transaction)) { - free(buf); - return 0; - } - err = GetLastError(); - } - - if (RemoveDirectoryTransactedW(buf, atomic->transaction) || - GetLastError() == ERROR_DIR_NOT_EMPTY) { - free(buf); - return 0; - } - - /* - * It would be tempting to use: - * MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) - * but unless we can guarantee that the system will be rebooted - * before we (or some other application) write another file with the - * same path, this is likely to cause more problems than it solves. - */ - - razor_atomic_set_error(atomic, buf, err); - free(buf); - return -1; -} - -RAZOR_EXPORT int -razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath, - const char *newpath) -{ - wchar_t *oldbuf, *newbuf; - const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING; - - if (atomic->error_str) - return -1; - - newbuf = razor_utf8_to_utf16(newpath, -1); - oldbuf = razor_utf8_to_utf16(oldpath, -1); - - /* - * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will - * cover every case we care about _except_ replacing an empty - * directory with a file. Calling RemoveDirectoryTransacted() will deal - * with this case while having no effect in all other cases. - */ - (void)RemoveDirectoryTransactedW(newbuf, atomic->transaction); - - if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags, - atomic->transaction)) - razor_atomic_set_error(atomic, newbuf, GetLastError()); - - free(newbuf); - free(oldbuf); - - return !!atomic->error_str; -} - -RAZOR_EXPORT int -razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname, - mode_t mode) -{ - wchar_t *buf; - DWORD err; - WIN32_FILE_ATTRIBUTE_DATA fa; - - if (atomic->error_str) - return -1; - - buf = razor_utf8_to_utf16(dirname, -1); - - if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) { - err = GetLastError(); - if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) { -abort: - razor_atomic_set_error(atomic, buf, err); - free(buf); - return -1; - } - - if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard, - &fa, atomic->transaction)) - goto abort; - - if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) { - if (razor_atomic_remove(atomic, dirname)) { - free(buf); - return -1; - } - if (!CreateDirectoryTransactedW(NULL, buf, NULL, - atomic->transaction)) { - err = GetLastError(); - goto abort; - } - } - } - - free(buf); - - return 0; -} - -RAZOR_EXPORT int -razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target, - const char *path) -{ - if (atomic->error_str) - return -1; - - /* - * This isn't true, but symbolic links under Windows 7 - * need to know whether the target is a directory or not - * and we don't always know that at the time when the - * link is created, so it's a convienent lie for now. - */ - razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported " - "on this platform"); - - return -1; -} - -RAZOR_EXPORT int -razor_atomic_create_file(struct razor_atomic *atomic, const char *filename, - mode_t mode) -{ - DWORD attribs; - struct razor_atomic_file *files; - int i = atomic->n_files; - - if (atomic->error_str) - return -1; - - files = realloc(atomic->files, - (atomic->n_files+1) * sizeof(struct razor_atomic_file)); - if (!files) { - razor_atomic_set_error_str(atomic, NULL, "Not enough memory"); - return -1; - } - atomic->n_files++; - atomic->files = files; - - files[i].path = razor_utf8_to_utf16(filename, -1); - - /* - * Passing CREATE_ALWAYS to CreateFileTransacted() will cover - * every case we care about _except_ replacing an empty directory - * with a file. Calling RemoveDirectoryTransacted() will deal - * with this case while having no effect in all other cases. - */ - (void)RemoveDirectoryTransactedW(files[i].path, atomic->transaction); - - if (mode & S_IWUSR) - attribs = FILE_ATTRIBUTE_NORMAL; - else - attribs = FILE_ATTRIBUTE_READONLY; - - files[i].h = CreateFileTransactedW(files[i].path, GENERIC_WRITE, - 0, NULL, CREATE_ALWAYS, attribs, - NULL, atomic->transaction, NULL, - NULL); - - if (files[i].h == INVALID_HANDLE_VALUE) { - razor_atomic_set_error(atomic, files[i].path, GetLastError()); - free(files[i].path); - atomic->n_files--; - return -1; - } - - return i; -} - -RAZOR_EXPORT int -razor_atomic_write(struct razor_atomic *atomic, int handle, const void *data, - size_t size) +razor_atomic_abort(struct razor_atomic *atomic, const char *error_msg) { - DWORD written; - - if (atomic->error_str) - return -1; - - assert(handle < atomic->n_files); - assert(atomic->files[handle].h != INVALID_HANDLE_VALUE); - - while(size) { - if (!WriteFile(atomic->files[handle].h, data, size, &written, - NULL)) { - razor_atomic_set_error(atomic, - atomic->files[handle].path, - GetLastError()); - - (void)CloseHandle(atomic->files[handle].h); - free(atomic->files[handle].path); - atomic->files[handle].path = NULL; - atomic->files[handle].h = INVALID_HANDLE_VALUE; - - return -1; - } - - data += written; - size -= written; - } - - return 0; + if (!atomic->error_str) + razor_atomic_set_error_str(atomic, NULL, error_msg); } RAZOR_EXPORT int -razor_atomic_sync(struct razor_atomic *atomic, int handle) +razor_atomic_in_error_state(struct razor_atomic *atomic) { - HANDLE h; - - if (atomic->error_str) - return -1; - - assert(handle < atomic->n_files); - assert(atomic->files[handle].h != INVALID_HANDLE_VALUE); - - if (!CloseHandle(atomic->files[handle].h)) { - razor_atomic_set_error(atomic, atomic->files[handle].path, - GetLastError()); - free(atomic->files[handle].path); - atomic->files[handle].path = NULL; - atomic->files[handle].h = INVALID_HANDLE_VALUE; - return -1; - } - - h = CreateFileTransactedW(atomic->files[handle].path, GENERIC_WRITE, 0, - NULL, OPEN_EXISTING, 0, NULL, - atomic->transaction, NULL, NULL); - atomic->files[handle].h = h; - - if (atomic->files[handle].h == INVALID_HANDLE_VALUE) { - razor_atomic_set_error(atomic, atomic->files[handle].path, - GetLastError()); - free(atomic->files[handle].path); - atomic->files[handle].path = NULL; - return -1; - } - - return !!atomic->error_str; + return atomic->error_str && !atomic->in_undo; } -RAZOR_EXPORT int -razor_atomic_close(struct razor_atomic *atomic, int handle) -{ - if (atomic->error_str) - return -1; - - assert(handle < atomic->n_files); - assert(atomic->files[handle].h != INVALID_HANDLE_VALUE); - - if (!CloseHandle(atomic->files[handle].h)) - razor_atomic_set_error(atomic, atomic->files[handle].path, - GetLastError()); - - free(atomic->files[handle].path); - atomic->files[handle].path = NULL; - atomic->files[handle].h = INVALID_HANDLE_VALUE; - - while(atomic->n_files > 0 && - atomic->files[atomic->n_files-1].h == INVALID_HANDLE_VALUE) - atomic->n_files--; +#if !HAVE_WINDOWS_KTM - return !!atomic->error_str; -} +/* + * Common code with atomic-none and atomic-emulate + */ -#else /* HAVE_WINDOWS_KVM */ +#define RAZOR_ASCII_ISALPHA(c) \ + ((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z') -static int +int razor_valid_root_name(const char *name) { - if (allow_all_root_names) { + if (razor_allow_all_root_names()) { #ifdef MSWIN_API return !strpbrk(name, "/\\"); #else @@ -665,36 +110,10 @@ razor_valid_root_name(const char *name) #endif } -struct razor_atomic { - char *error_path; - char *error_str; - char *error_msg; -}; - -RAZOR_EXPORT struct razor_atomic * -razor_atomic_open(const char *description) -{ - struct razor_atomic *atomic; - - atomic = zalloc(sizeof *atomic); - - return atomic; -} - -static void -razor_atomic_set_error_str(struct razor_atomic *atomic, const char *path, - const char *str) -{ - assert(!atomic->error_str); - - atomic->error_path = path ? strdup(path) : NULL; - atomic->error_str = strdup(str); -} - #ifdef MSWIN_API -static void +void razor_atomic_set_error_mswin(struct razor_atomic *atomic, const wchar_t *path, - DWORD error) + DWORD error) { wchar_t *buf; @@ -716,198 +135,14 @@ razor_atomic_set_error_mswin(struct razor_atomic *atomic, const wchar_t *path, } #endif -RAZOR_EXPORT int -razor_atomic_commit(struct razor_atomic *atomic) -{ - return !!atomic->error_str; -} - -RAZOR_EXPORT void -razor_atomic_destroy(struct razor_atomic *atomic) -{ - free(atomic->error_path); - free(atomic->error_str); - free(atomic->error_msg); - free(atomic); -} - -RAZOR_EXPORT int -razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root, - const char *path) -{ - char buffer[PATH_MAX], *p; - const char *slash, *next; - struct stat buf; - - if (atomic->error_str) - return -1; - - strcpy(buffer, root); - p = buffer + strlen(buffer); - slash = path; - for (slash = path; *slash != '\0'; slash = next) { -#ifdef MSWIN_API - next = strpbrk(slash + 1, "/\\"); -#else - next = strchr(slash + 1, '/'); -#endif - if (next == NULL) - break; - - memcpy(p, slash, next - slash); - p += next - slash; - *p = '\0'; - - if (razor_valid_root_name(buffer)) - continue; - - if (stat(buffer, &buf) == 0) { - if (!S_ISDIR(buf.st_mode)) { - razor_atomic_set_error_str(atomic, buffer, - "Not a directory"); - return -1; - } - } else if (mkdir(buffer, 0777) < 0) { - razor_atomic_set_error_str(atomic, buffer, - strerror(errno)); - return -1; - } - } - - return 0; -} - -RAZOR_EXPORT int -razor_atomic_remove(struct razor_atomic *atomic, const char *path) -{ -#ifdef MSWIN_API - wchar_t *buf; - DWORD err; -#endif - - if (atomic->error_str) - return -1; - -#ifdef MSWIN_API - buf = razor_utf8_to_utf16(path, -1); - - if (!DeleteFileW(buf)) { - err = GetLastError(); - if (err != ERROR_FILE_NOT_FOUND && - err != ERROR_PATH_NOT_FOUND && - !(SetFileAttributesW(buf, FILE_ATTRIBUTE_NORMAL) && - DeleteFileW(buf)) && - !RemoveDirectoryW(buf) && - GetLastError() != ERROR_DIR_NOT_EMPTY) - razor_atomic_set_error_mswin(atomic, buf, err); - } - - free(buf); -#else - if (remove(path)) - razor_atomic_set_error_str(atomic, path, strerror(errno)); -#endif - - return !!atomic->error_str; -} - -RAZOR_EXPORT int -razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath, - const char *newpath) -{ -#ifdef MSWIN_API - wchar_t *oldbuf, *newbuf; - const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING; -#endif - - if (atomic->error_str) - return -1; - -#ifdef MSWIN_API - newbuf = razor_utf8_to_utf16(newpath, -1); - oldbuf = razor_utf8_to_utf16(oldpath, -1); - - /* - * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will - * cover every case we care about _except_ replacing an empty - * directory with a file. Calling RemoveDirectory() will deal - * with this case while having no effect in all other cases. - */ - (void)RemoveDirectoryW(newbuf); - - if (!MoveFileExW(oldbuf, newbuf, flags)) - razor_atomic_set_error_mswin(atomic, newbuf, GetLastError()); - - free(newbuf); - free(oldbuf); -#else - if (rename(oldpath, newpath)) - razor_atomic_set_error_str(atomic, newpath, strerror(errno)); -#endif - - return !!atomic->error_str; -} - -RAZOR_EXPORT int -razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname, - mode_t mode) -{ - if (atomic->error_str) - return -1; - - if (!mkdir(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO))) - return 0; - - if (errno != EEXIST) { - razor_atomic_set_error_str(atomic, dirname, strerror(errno)); - return -1; - } - - if (chmod(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)) < 0) { - razor_atomic_set_error_str(atomic, dirname, strerror(errno)); - return -1; - } - - return 0; -} - -RAZOR_EXPORT int -razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target, - const char *path) -{ - if (atomic->error_str) - return -1; - -#if HAVE_SYMLINK - if (symlink(target, path) < 0) { - razor_atomic_set_error_str(atomic, NULL, strerror(errno)); - return -1; - } -#else - razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported " - "on this platform"); -#endif - - return 0; -} - -RAZOR_EXPORT int -razor_atomic_create_file(struct razor_atomic *atomic, const char *filename, - mode_t mode) +void +razor_atomic_set_error_str(struct razor_atomic *atomic, const char *path, + const char *str) { - int fd; - - if (atomic->error_str) - return -1; - - atomic->error_path = strdup(filename); - fd = open(atomic->error_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, - mode & (S_IRWXU | S_IRWXG | S_IRWXO)); - - if (fd == -1) - razor_atomic_set_error_str(atomic, NULL, strerror(errno)); + assert(!atomic->error_str); - return fd; + atomic->error_path = path ? strdup(path) : NULL; + atomic->error_str = strdup(str); } RAZOR_EXPORT int @@ -916,13 +151,14 @@ razor_atomic_write(struct razor_atomic *atomic, int fd, const void *data, { int written; - if (atomic->error_str) + if (razor_atomic_in_error_state(atomic)) return -1; while(size) { written = write(fd, data, size); if (written < 0) { - razor_atomic_set_error_str(atomic, NULL, strerror(errno)); + razor_atomic_set_error_str(atomic, NULL, + strerror(errno)); (void)close(fd); @@ -939,7 +175,7 @@ razor_atomic_write(struct razor_atomic *atomic, int fd, const void *data, RAZOR_EXPORT int razor_atomic_sync(struct razor_atomic *atomic, int handle) { - if (atomic->error_str) + if (razor_atomic_in_error_state(atomic)) return -1; if (fsync(handle) < 0) { @@ -956,7 +192,7 @@ razor_atomic_sync(struct razor_atomic *atomic, int handle) RAZOR_EXPORT int razor_atomic_close(struct razor_atomic *atomic, int fd) { - if (atomic->error_str) + if (razor_atomic_in_error_state(atomic)) return -1; if (close(fd) < 0) { @@ -970,33 +206,4 @@ razor_atomic_close(struct razor_atomic *atomic, int fd) return 0; } -#endif /* HAVE_WINDOWS_KVM */ - -RAZOR_EXPORT const char * -razor_atomic_get_error_msg(struct razor_atomic *atomic) -{ - if (!atomic->error_msg) { - if (atomic->error_path) - atomic->error_msg = razor_concat(atomic->error_path, - ": ", - atomic->error_str, - NULL); - else - atomic->error_msg = strdup(atomic->error_str); - } - - return atomic->error_msg; -} - -RAZOR_EXPORT void -razor_atomic_abort(struct razor_atomic *atomic, const char *error_msg) -{ - if (!atomic->error_str) - razor_atomic_set_error_str(atomic, NULL, error_msg); -} - -RAZOR_EXPORT int -razor_atomic_in_error_state(struct razor_atomic *atomic) -{ - return !!atomic->error_str; -} +#endif /* !HAVE_WINDOWS_KTM */ diff --git a/librazor/razor-internal.h b/librazor/razor-internal.h index 22f193b..790429d 100644 --- a/librazor/razor-internal.h +++ b/librazor/razor-internal.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009, 2011 J. Ali Harlow + * Copyright (C) 2009, 2011-2012 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,9 @@ #ifndef _RAZOR_INTERNAL_H_ #define _RAZOR_INTERNAL_H_ +#ifdef MSWIN_API +#include +#endif #include #include #include @@ -234,4 +237,109 @@ void environment_set(struct environment *env); void environment_unset(struct environment *env); void environment_release(struct environment *env); +#ifdef MSWIN_API +char *razor_utf16_to_utf8(const wchar_t *utf16, int len); +wchar_t *razor_utf8_to_utf16(const char *utf8, int len); +#endif + +/* Atomic functions */ + +#if HAVE_WINDOWS_KTM +struct razor_atomic { + HANDLE transaction; + int n_files; + struct razor_atomic_file { + wchar_t *path; + HANDLE h; + } *files; + int in_undo; + char *error_path; + char *error_str; + char *error_msg; +}; +#elif ENABLE_ATOMIC +struct atomic_action { + struct atomic_action *next; + enum atomic_action_type { + /* Complex actions */ + ACTION_MAKE_DIRS, + ACTION_REMOVE, + /* Primitive actions */ + ACTION_CREATE_DIR, +#if HAVE_SYMLINK + ACTION_CREATE_SYMLINK, +#endif + ACTION_MOVE, + } type; + struct { + char *path; + union atomic_action_args { + struct { + char *root; + } make_dirs; + struct { + mode_t mode; + } create_dir; +#if HAVE_SYMLINK + struct { + char *target; + } create_symlink; +#endif + struct { + char *dest; + } move; + } u; + } args; +}; + +struct razor_atomic { + struct atomic_action *actions; + char *description; + char *toplevel; + unsigned next_file_tag; + int in_undo; + char *error_path; + char *error_str; + char *error_msg; +}; + +char *atomic_action_attic_tmpnam(struct razor_atomic *atomic); +struct atomic_action * +atomic_action_list_prepend(struct atomic_action *list, + struct atomic_action *action); +struct atomic_action *atomic_action_new(enum atomic_action_type type); +void atomic_action_free(struct atomic_action *action); +struct atomic_action *atomic_action_list_reverse(struct atomic_action *list); +struct atomic_action * +atomic_action_do(struct razor_atomic *atomic, struct atomic_action *action); +void +atomic_action_undo(struct razor_atomic *atomic, struct atomic_action *action); +#else /* !HAVE_WINDOWS_KTM && !ENABLE_ATOMIC */ +struct razor_atomic { + int in_undo; + char *error_path; + char *error_str; + char *error_msg; +}; +#endif + +int razor_allow_all_root_names(void); +int razor_valid_root_name(const char *name); + +#ifdef MSWIN_API +void +razor_atomic_set_error_mswin(struct razor_atomic *atomic, const wchar_t *path, + DWORD error); +#endif + +#if HAVE_WINDOWS_KTM +void +razor_atomic_set_error_str(struct razor_atomic *atomic, const wchar_t *path, + const char *str); +#else +void +razor_atomic_set_error_str(struct razor_atomic *atomic, const char *path, + const char *str); +#endif + #endif /* _RAZOR_INTERNAL_H_ */ diff --git a/librazor/razor.h b/librazor/razor.h index 0806091..52cac4a 100644 --- a/librazor/razor.h +++ b/librazor/razor.h @@ -104,8 +104,13 @@ enum razor_property_flags { * that should either all succeed or all fail. * * Note that currently only Windows 7 has a native implementation and that - * the fallback implementation will not rollback even if an error does occur. - * This could (and should) be improved. + * while the emulated fallback implementation attempts to rollback the + * transaction if it fails, this is not guaranteed to succeed and that + * the transaction is held in core (and thus vulnernable to program crashes, + * power loss, etc.). This could (and should) be improved. + * + * Atomic transactions can be disabled via configure, in which case all + * bets are off. **/ struct razor_atomic; @@ -331,7 +336,8 @@ struct razor_install_iterator; enum razor_install_action { RAZOR_INSTALL_ACTION_ADD, - RAZOR_INSTALL_ACTION_REMOVE + RAZOR_INSTALL_ACTION_REMOVE, + RAZOR_INSTALL_ACTION_COMMIT }; struct razor_install_iterator * diff --git a/librazor/util.c b/librazor/util.c index dba6b7a..047a5d6 100644 --- a/librazor/util.c +++ b/librazor/util.c @@ -349,3 +349,39 @@ RAZOR_EXPORT const char *razor_system_arch(void) return un.machine; #endif } + +#ifdef MSWIN_API + +char *razor_utf16_to_utf8(const wchar_t *utf16, int len) +{ + int n; + char *utf8; + + n = WideCharToMultiByte(CP_UTF8, 0, utf16, len, NULL, 0, NULL, NULL); + if (len >= 0 && utf16[len]) + n++; + utf8 = malloc(n); + (void)WideCharToMultiByte(CP_UTF8, 0, utf16, len, utf8, n, NULL, NULL); + if (len >= 0 && utf16[len]) + utf8[n - 1] = 0; + + return utf8; +} + +wchar_t *razor_utf8_to_utf16(const char *utf8, int len) +{ + int n; + wchar_t *utf16; + + n = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0); + if (len >= 0 && utf8[len]) + n++; + utf16 = malloc(n * sizeof(wchar_t)); + (void)MultiByteToWideChar(CP_UTF8, 0, utf8, len, utf16, n); + if (len >= 0 && utf8[len]) + utf16[n - 1] = 0; + + return utf16; +} + +#endif /* MSWIN_API */ -- 1.7.1