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,
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"
importer.c \
merger.c \
atomic.c \
+ atomic-ktm.c \
+ atomic-none.c \
+ atomic-emulate.c \
+ atomic-actions.c \
transaction.c
if HAVE_LUA
--- /dev/null
+/*
+ * Copyright (C) 2012 J. Ali Harlow <ali@juiblex.co.uk>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <assert.h>
+#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 */
--- /dev/null
+/*
+ * Copyright (C) 2012 J. Ali Harlow <ali@juiblex.co.uk>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#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 */
--- /dev/null
+/*
+ * Copyright (C) 2011-2012 J. Ali Harlow <ali@juiblex.co.uk>
+ *
+ * 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 <stdlib.h>
+#include <windows.h>
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <assert.h>
+#include <wchar.h>
+#include <ktmw32.h>
+
+#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 */
--- /dev/null
+/*
+ * Copyright (C) 2011-2012 J. Ali Harlow <ali@juiblex.co.uk>
+ *
+ * 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 <stdlib.h>
+#ifdef MSWIN_API
+#include <windows.h>
+#endif
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <assert.h>
+
+#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 */
/*
- * Copyright (C) 2011 J. Ali Harlow <ali@juiblex.co.uk>
+ * Copyright (C) 2011-2012 J. Ali Harlow <ali@juiblex.co.uk>
*
* 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
#include "config.h"
#include <stdlib.h>
-#ifdef MSWIN_API
-#include <windows.h>
-#endif
#include <stdio.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
-#include <fcntl.h>
+#include <sys/types.h>
#include <sys/stat.h>
+#include <fcntl.h>
#include <string.h>
#include <assert.h>
-#if HAVE_WINDOWS_KTM
-#include <wchar.h>
-#include <ktmw32.h>
-#endif
#include "razor.h"
#include "razor-internal.h"
* 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;
/*
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
#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;
}
#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
{
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);
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) {
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) {
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 */
/*
* Copyright (C) 2008 Kristian Høgsberg <krh@redhat.com>
* Copyright (C) 2008 Red Hat, Inc
- * Copyright (C) 2009, 2011 J. Ali Harlow <ali@juiblex.co.uk>
+ * Copyright (C) 2009, 2011-2012 J. Ali Harlow <ali@juiblex.co.uk>
*
* 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
#ifndef _RAZOR_INTERNAL_H_
#define _RAZOR_INTERNAL_H_
+#ifdef MSWIN_API
+#include <windows.h>
+#endif
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
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_ */
* 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;
enum razor_install_action {
RAZOR_INSTALL_ACTION_ADD,
- RAZOR_INSTALL_ACTION_REMOVE
+ RAZOR_INSTALL_ACTION_REMOVE,
+ RAZOR_INSTALL_ACTION_COMMIT
};
struct razor_install_iterator *
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 */