Use Windows KTM (atomic transactions) where supported. 0.5
authorJ. Ali Harlow <ali@juiblex.co.uk>
Thu, 10 Nov 2011 10:35:21 +0000 (10:35 +0000)
committerJ. Ali Harlow <ali@juiblex.co.uk>
Thu, 10 Nov 2011 10:35:21 +0000 (10:35 +0000)
Increment current header version to 2

13 files changed:
configure.ac
librazor/Makefile.am
librazor/atomic.c [new file with mode: 0644]
librazor/merger.c
librazor/razor-internal.h
librazor/razor.c
librazor/razor.h
librazor/root.c
librazor/rpm.c
librazor/transaction.c
librazor/util.c
src/main.c
src/rpm.c

index 6331fae..bb36d89 100644 (file)
@@ -1,7 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.59c)
-AC_INIT([razor], [0.4.1], [ali@juiblex.co.uk])
+AC_INIT([razor], [0.5], [ali@juiblex.co.uk])
 AM_INIT_AUTOMAKE([])
 AM_CONFIG_HEADER([config.h])
 AM_MAINTAINER_MODE
@@ -10,8 +10,8 @@ AM_MAINTAINER_MODE
 #
 # See http://sources.redhat.com/autobook/autobook/autobook_91.html#SEC91 for details
 #
-LT_CURRENT=1
-LT_REVISION=2
+LT_CURRENT=2
+LT_REVISION=0
 LT_AGE=0
 AC_SUBST(LT_CURRENT)
 AC_SUBST(LT_REVISION)
@@ -45,6 +45,25 @@ AC_MSG_RESULT([$mswin_api])
 AM_CONDITIONAL(MSWIN_API, test "$mswin_api" = "yes")
 AC_SUBST(EXTRA_LIBS)
 
+if test "$mswin_api" = "yes"; then
+    AC_MSG_CHECKING([for Microsoft Windows Kernel Transaction Manager])
+    save_LIBS="$LIBS"
+    LIBS="-lktmw32 $LIBS"
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl
+#include <windows.h>
+#include <ktmw32.h>
+], [(void)CreateTransaction(NULL,0,0,0,0,0,NULL);])],
+      [have_windows_ktm="yes"; EXTRA_LIBS="-lktmw32 $EXTRA_LIBS"],
+      [have_windows_ktm="no"])
+    LIBS="$save_LIBS"
+    AC_MSG_RESULT([$have_windows_ktm])
+else
+    have_windows_ktm="no"
+fi
+if test "$have_windows_ktm" = "yes"; then
+    AC_DEFINE([HAVE_WINDOWS_KTM],[1],[Define if Windows KTM is available.])
+fi
+
 # Taken from dbus
 AC_ARG_ENABLE(ansi,             [  --enable-ansi           enable -ansi -pedantic gcc flags],enable_ansi=$enableval,enable_ansi=no)
 AC_ARG_ENABLE(verbose-mode,     [  --enable-verbose-mode   support verbose debug mode],enable_verbose_mode=$enableval,enable_verbose_mode=$USE_MAINTAINER_MODE)
index e887ad3..bc5c469 100644 (file)
@@ -33,6 +33,7 @@ librazor_la_SOURCES =                                 \
        iterator.c                                      \
        importer.c                                      \
        merger.c                                        \
+       atomic.c                                        \
        transaction.c
 
 if HAVE_LUA
diff --git a/librazor/atomic.c b/librazor/atomic.c
new file mode 100644 (file)
index 0000000..ffc5bbf
--- /dev/null
@@ -0,0 +1,1002 @@
+/*
+ * Copyright (C) 2011  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"
+
+#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>
+#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;
+
+/*
+ * Primarily intended for testing named roots under UNIX platforms.
+ */
+RAZOR_EXPORT void
+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)
+{
+       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 (atomic->error_str)
+               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, *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)
+{
+       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;
+}
+
+RAZOR_EXPORT int
+razor_atomic_sync(struct razor_atomic *atomic, int handle)
+{
+       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;
+}
+
+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--;
+
+       return !!atomic->error_str;
+}
+
+#else          /* HAVE_WINDOWS_KVM */
+
+static int
+razor_valid_root_name(const char *name)
+{
+       if (allow_all_root_names) {
+#ifdef MSWIN_API
+               return !strpbrk(name, "/\\");
+#else
+               return !strchr(name, '/');
+#endif
+       }
+
+#ifdef MSWIN_API
+       return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
+              name[2] == '\0';
+#else
+       return name[0] == '\0';
+#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
+razor_atomic_set_error_mswin(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);
+}
+#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)
+{
+       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));
+
+       return fd;
+}
+
+RAZOR_EXPORT int
+razor_atomic_write(struct razor_atomic *atomic, int fd, const void *data,
+                  size_t size)
+{
+       int written;
+
+       if (atomic->error_str)
+               return -1;
+
+       while(size) {
+               written = write(fd, data, size);
+               if (written < 0) {
+                       razor_atomic_set_error_str(atomic, NULL, strerror(errno));
+
+                       (void)close(fd);
+
+                       return -1;
+               }
+
+               data += written;
+               size -= written;
+       }
+
+       return 0;
+}
+
+RAZOR_EXPORT int
+razor_atomic_sync(struct razor_atomic *atomic, int handle)
+{
+       if (atomic->error_str)
+               return -1;
+
+       if (fsync(handle) < 0) {
+               razor_atomic_set_error_str(atomic, NULL, strerror(errno));
+               return -1;
+       }
+
+       free(atomic->error_path);
+       atomic->error_path = NULL;
+
+       return 0;
+}
+
+RAZOR_EXPORT int
+razor_atomic_close(struct razor_atomic *atomic, int fd)
+{
+       if (atomic->error_str)
+               return -1;
+
+       if (close(fd) < 0) {
+               razor_atomic_set_error_str(atomic, NULL, strerror(errno));
+               return -1;
+       }
+
+       free(atomic->error_path);
+       atomic->error_path = NULL;
+
+       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;
+}
index ebf1c86..4fcd36d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
  * Copyright (C) 2008  Red Hat, Inc
- * Copyright (C) 2009, 2010  J. Ali Harlow <ali@juiblex.co.uk>
+ * Copyright (C) 2009-2011  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
@@ -47,9 +47,18 @@ razor_merger_create(struct razor_set *set1, struct razor_set *set2)
        struct razor_merger *merger;
        int count;
        size_t size;
+       uint32_t header_version;
 
        merger = zalloc(sizeof *merger);
        merger->set = razor_set_create();
+       if (set1->packages.size) {
+               header_version = razor_set_get_header_version(set1);
+               if (set2->packages.size &&
+                   razor_set_get_header_version(set2) > header_version)
+                       header_version = razor_set_get_header_version(set2);
+       } else
+               header_version = razor_set_get_header_version(set2);
+       razor_set_set_header_version(merger->set, header_version);
        hashtable_init(&merger->table, &merger->set->string_pool);
        hashtable_init(&merger->file_table, &merger->set->file_string_pool);
        hashtable_init(&merger->details_table,
@@ -592,16 +601,24 @@ razor_merger_package_add_script(struct razor_merger *merger,
 
        switch (script) {
        case RAZOR_PROPERTY_PREUN:
-               assert(pool[package->preun.program] == '\0');
-               assert(pool[package->preun.body] == '\0');
-               package->preun.program = p;
-               package->preun.body = b;
+               if (package->preun.program != p) {
+                       assert(pool[package->preun.program] == '\0');
+                       package->preun.program = p;
+               }
+               if (package->preun.body != b) {
+                       assert(pool[package->preun.body] == '\0');
+                       package->preun.body = b;
+               }
                break;
        case RAZOR_PROPERTY_POSTUN:
-               assert(pool[package->postun.program] == '\0');
-               assert(pool[package->postun.body] == '\0');
-               package->postun.program = p;
-               package->postun.body = b;
+               if (package->postun.program != p) {
+                       assert(pool[package->postun.program] == '\0');
+                       package->postun.program = p;
+               }
+               if (package->postun.body != b) {
+                       assert(pool[package->postun.body] == '\0');
+                       package->postun.body = b;
+               }
                break;
        default:
                break;
index bc2b7bf..22f193b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
  * Copyright (C) 2008  Red Hat, Inc
- * Copyright (C) 2009  J. Ali Harlow <ali@juiblex.co.uk>
+ * Copyright (C) 2009, 2011  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
@@ -29,7 +29,7 @@
 #include "razor.h"
 #include "types/types.h"
 
-/* GCC visibility */
+/* GCC extensions */
 #if defined(__GNUC__) && __GNUC__ >= 4
 #define RAZOR_EXPORT __attribute__ ((visibility("default")))
 #else
@@ -54,7 +54,6 @@ struct razor_set_header {
 };
 
 #define RAZOR_MAGIC    0x525a4442
-#define RAZOR_VERSION  1
 
 #define RAZOR_STRING_POOL              "string_pool"
 #define RAZOR_PACKAGES                 "packages"
@@ -108,6 +107,7 @@ struct razor_entry {
 #define RAZOR_ENTRY_LAST       0x80
 
 struct razor_set {
+       uint32_t header_version;
        struct array string_pool;
        struct array packages;
        struct array properties;
@@ -119,7 +119,7 @@ struct razor_set {
        struct array file_string_pool;
        struct array details_string_pool;
        struct razor_mapped_file *mapped_files;
-       int lock_fd;
+       int lock_fd, ref_count;
 };
 
 struct import_entry {
index 5a6264e..228fbf9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
  * Copyright (C) 2008  Red Hat, Inc
- * Copyright (C) 2009, 2010  J. Ali Harlow <ali@juiblex.co.uk>
+ * Copyright (C) 2009-2011  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
@@ -93,10 +93,16 @@ razor_set_create_without_root(void)
 
        set = zalloc(sizeof *set);
 
-       empty = array_add(&set->string_pool, 1);
-       *empty = '\0';
+       if (set) {
+               empty = array_add(&set->string_pool, 1);
+               *empty = '\0';
 
-       set->lock_fd = -1;
+               set->lock_fd = -1;
+
+               set->ref_count = 1;
+
+               set->header_version = RAZOR_HEADER_VERSION;
+       }
 
        return set;
 }
@@ -118,6 +124,24 @@ razor_set_create(void)
        return set;
 }
 
+RAZOR_EXPORT uint32_t
+razor_set_get_header_version(struct razor_set *set)
+{
+       return set->header_version;
+}
+
+RAZOR_EXPORT int
+razor_set_set_header_version(struct razor_set *set, uint32_t header_version)
+{
+       if (header_version<RAZOR_HEADER_VERSION_MIN ||
+           header_version>RAZOR_HEADER_VERSION)
+               return -1;
+       else {
+               set->header_version = header_version;
+               return 0;
+       }
+}
+
 struct razor_mapped_file {
        struct razor_set_header *header;
        size_t size;
@@ -125,32 +149,52 @@ struct razor_mapped_file {
 };
 
 RAZOR_EXPORT int
-razor_set_bind_sections(struct razor_set *set, const char *filename)
+razor_set_bind_sections(struct razor_set *set, struct razor_atomic *atomic,
+                       const char *filename)
 {
        struct razor_set_section *s, *sections;
        struct razor_mapped_file *file;
-       const char *pool;
+       const char *pool, *reason;
+       char *msg;
        struct array *array;
        int i, j;
 
        file = zalloc(sizeof *file);
-       if (file == NULL)
+       if (file == NULL) {
+               razor_atomic_abort(atomic, "Not enough memory");
                return -1;
+       }
 
        file->header = razor_file_get_contents(filename, &file->size);
        if (!file->header) {
+               msg = razor_concat(filename, ": ", strerror(errno), NULL);
+               razor_atomic_abort(atomic, msg);
+               free(msg);
                free(file);
                return -1;
        }
 
-       if (file->size < sizeof *file->header ||
-           file->header->magic != RAZOR_MAGIC ||
-           file->header->version != RAZOR_VERSION) {
+       if (file->size < sizeof *file->header)
+               reason = "Premature EOF";
+       else if (file->header->magic != RAZOR_MAGIC)
+               reason = "Bad magic number";
+       else if (file->header->version < RAZOR_HEADER_VERSION_MIN ||
+                file->header->version > RAZOR_HEADER_VERSION)
+               reason = "Incompatible file version";
+       else
+               reason = NULL;
+
+       if (reason) {
+               msg = razor_concat(filename, ": ", reason, NULL);
+               razor_atomic_abort(atomic, msg);
+               free(msg);
                razor_file_free_contents(file->header, file->size);
                free(file);
                return -1;
        }
 
+       set->header_version = file->header->version;
+
        if (set->mapped_files == NULL) {
                for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
                        array = (void *) set + razor_sections[i].offset;
@@ -182,13 +226,18 @@ razor_set_bind_sections(struct razor_set *set, const char *filename)
 }
 
 RAZOR_EXPORT struct razor_set *
-razor_set_open(const char *filename)
+razor_set_open(const char *filename, struct razor_atomic *atomic)
 {
        struct razor_set *set;
 
        set = zalloc(sizeof *set);
+       if (!set) {
+               razor_atomic_abort(atomic, "Not enough memory");
+               return NULL;
+       }
+
        set->lock_fd = -1;
-       if (razor_set_bind_sections(set, filename)){
+       if (razor_set_bind_sections(set, atomic, filename)) {
                free(set);
                return NULL;
        }
@@ -215,12 +264,14 @@ razor_set_aquire_lock(struct razor_set *set, const char *path, int exclusive)
 
        if (exclusive)
                flags |= LOCKFILE_EXCLUSIVE_LOCK;
-       if (fd >= 0 && !LockFileEx(_get_osfhandle(fd), flags, 0, 1, 0, &lock)) {
+       if (fd >= 0 && !LockFileEx((HANDLE)_get_osfhandle(fd), flags, 0, 1, 0,
+                                  &lock)) {
                close(fd);
                return -1;
        }
        if (set->lock_fd >= 0)
-               (void)UnlockFile(_get_osfhandle(set->lock_fd), 0, 0, 1, 0);
+               (void)UnlockFile((HANDLE)_get_osfhandle(set->lock_fd), 0, 0, 1,
+                                0);
 #else
        struct flock lock = {0};
 
@@ -245,7 +296,7 @@ razor_set_aquire_lock(struct razor_set *set, const char *path, int exclusive)
        return 0;
 }
 
-RAZOR_EXPORT void
+static void
 razor_set_destroy(struct razor_set *set)
 {
        struct razor_mapped_file *file, *next;
@@ -271,8 +322,24 @@ razor_set_destroy(struct razor_set *set)
        free(set);
 }
 
-RAZOR_EXPORT int
-razor_set_write_to_fd(struct razor_set *set, int fd, uint32_t section_mask)
+RAZOR_EXPORT void
+razor_set_unref(struct razor_set *set)
+{
+       if (set && !--set->ref_count)
+               razor_set_destroy(set);
+}
+
+RAZOR_EXPORT struct razor_set *
+razor_set_ref(struct razor_set *set)
+{
+       if (set)
+               set->ref_count++;
+       return set;
+}
+
+RAZOR_EXPORT void
+razor_set_write_to_handle(struct razor_set *set, struct razor_atomic *atomic,
+                         int handle, uint32_t section_mask)
 {
        struct razor_set_header header;
        struct razor_set_section sections[ARRAY_SIZE(razor_sections)];
@@ -298,7 +365,7 @@ razor_set_write_to_fd(struct razor_set *set, int fd, uint32_t section_mask)
 
        count = j;
        header.magic = RAZOR_MAGIC;
-       header.version = RAZOR_VERSION;
+       header.version = set->header_version;
        header.num_sections = count;
        offset = sizeof header + count * sizeof *sections + ALIGN(pool.size, 4);
 
@@ -308,38 +375,36 @@ razor_set_write_to_fd(struct razor_set *set, int fd, uint32_t section_mask)
                offset += ALIGN(arrays[i]->size, 4);
        }
 
-       razor_write(fd, &header, sizeof header);
-       razor_write(fd, sections, count * sizeof *sections);
-       razor_write(fd, pool.data, pool.size);
-       razor_write(fd, padding, PADDING(pool.size, 4));
+       razor_atomic_write(atomic, handle, &header, sizeof header);
+       razor_atomic_write(atomic, handle, sections, count * sizeof *sections);
+       razor_atomic_write(atomic, handle, pool.data, pool.size);
+       razor_atomic_write(atomic, handle, padding, PADDING(pool.size, 4));
 
        for (i = 0; i < count; i++) {
-               razor_write(fd, arrays[i]->data, arrays[i]->size);
-               razor_write(fd, padding, PADDING(arrays[i]->size, 4));
+               razor_atomic_write(atomic, handle, arrays[i]->data,
+                                  arrays[i]->size);
+               razor_atomic_write(atomic, handle, padding,
+                                  PADDING(arrays[i]->size, 4));
        }
 
        array_release(&pool);
        hashtable_release(&table);
-
-       return 0;
 }
 
 RAZOR_EXPORT int
-razor_set_write(struct razor_set *set, const char *filename, uint32_t sections)
+razor_set_write(struct razor_set *set, struct razor_atomic *atomic,
+               const char *filename, uint32_t sections)
 {
-       int fd, status;
+       int h;
 
-       fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666);
-       if (fd < 0)
+       h = razor_atomic_create_file(atomic, filename,
+                                    S_IRWXU | S_IRWXG | S_IRWXO);
+       if (h < 0)
                return -1;
 
-       status = razor_set_write_to_fd(set, fd, sections);
-       if (status) {
-           close(fd);
-           return status;
-       }
+       razor_set_write_to_handle(set, atomic, h, sections);
 
-       return close(fd);
+       return razor_atomic_close(atomic, h);
 }
 
 RAZOR_EXPORT void
@@ -554,74 +619,86 @@ razor_package_get_details(struct razor_set *set, struct razor_package *package,
  * @package: a %razor_package
  * @root: the root into which the package is currently installed
  * @install_count: the value to pass to uninstall scripts
+ * @stage: Limit the removal to just the scripts or the files
  *
  * Removes an installed package.
  **/
 RAZOR_EXPORT int
 razor_package_remove(struct razor_set *prev, struct razor_set *next,
-                    struct razor_package *package, const char *root,
-                    int install_count)
+                    struct razor_atomic *atomic, struct razor_package *package,
+                    const char *root, int install_count,
+                    enum razor_stage_type stage)
 {
        struct razor_file_iterator *fi;
        struct razor_package_iterator *pi;
        struct razor_package *p;
-       char buffer[PATH_MAX];
+       char *buffer;
        const char *name, *program, *script;
-       int retval = 0, i, count;
+       int i, count;
        struct environment env;
        struct list *link;
        const char *prefix;
 
-       environment_init(&env);
-       link = list_first(&package->install_prefixes, &prev->prefix_pool);
-       for (i = 0; link; i++) {
-               prefix = (const char *)prev->string_pool.data + link->data;
-               sprintf(buffer, "RPM_INSTALL_PREFIX%d", i);
-               environment_add_variable(&env, buffer, prefix);
-               link = list_next(link);
+       if (stage & RAZOR_STAGE_SCRIPTS) {
+               environment_init(&env);
+               link = list_first(&package->install_prefixes,
+                                 &prev->prefix_pool);
+               for (i = 0; link; i++) {
+                       prefix = (const char *)prev->string_pool.data +
+                                link->data;
+                       sprintf(buffer, "RPM_INSTALL_PREFIX%d", i);
+                       environment_add_variable(&env, buffer, prefix);
+                       link = list_next(link);
+               }
+               environment_set(&env);
        }
-       environment_set(&env);
 
-       razor_package_get_details(prev, package,
-                                 RAZOR_DETAIL_PREUNPROG, &program,
-                                 RAZOR_DETAIL_PREUN, &script,
-                                 RAZOR_DETAIL_LAST);
-
-       retval = razor_run_script(root, RAZOR_PROPERTY_PREUN, program, script,
-                                 install_count);
+       if (stage & RAZOR_STAGE_SCRIPTS_PRE) {
+               razor_package_get_details(prev, package,
+                                         RAZOR_DETAIL_PREUNPROG, &program,
+                                         RAZOR_DETAIL_PREUN, &script,
+                                         RAZOR_DETAIL_LAST);
 
-       fi = razor_file_iterator_create(prev, package, 1);
+               razor_run_script(root, RAZOR_PROPERTY_PREUN, program,
+                                script, install_count);
+       }
 
-       while (razor_file_iterator_next(fi, &name)) {
-               pi = razor_package_iterator_create_for_file(next, name);
-               count = 0;
-               while (razor_package_iterator_next(pi, &p, RAZOR_DETAIL_LAST))
-                       count++;
-               razor_package_iterator_destroy(pi);
-               if (count <= 0) {
-                       snprintf(buffer, sizeof buffer, "%s%s", root, name);
-                       if (razor_remove(buffer) && errno != ENOENT) {
-                               perror(name);
-                               retval = -1;
+       if (stage & RAZOR_STAGE_FILES) {
+               fi = razor_file_iterator_create(prev, package, 1);
+
+               while (razor_file_iterator_next(fi, &name)) {
+                       pi = razor_package_iterator_create_for_file(next, name);
+                       count = 0;
+                       while (razor_package_iterator_next(pi, &p,
+                                                          RAZOR_DETAIL_LAST))
+                               count++;
+                       razor_package_iterator_destroy(pi);
+                       if (count <= 0) {
+                               buffer = razor_concat(root, name, NULL);
+                               razor_atomic_remove(atomic, buffer);
+                               free(buffer);
                        }
                }
-       }
 
-       razor_file_iterator_destroy(fi);
+               razor_file_iterator_destroy(fi);
+       }
 
-       razor_package_get_details(prev, package,
-                                 RAZOR_DETAIL_POSTUNPROG, &program,
-                                 RAZOR_DETAIL_POSTUN, &script,
-                                 RAZOR_DETAIL_LAST);
+       if (stage & RAZOR_STAGE_SCRIPTS_POST) {
+               razor_package_get_details(prev, package,
+                                         RAZOR_DETAIL_POSTUNPROG, &program,
+                                         RAZOR_DETAIL_POSTUN, &script,
+                                         RAZOR_DETAIL_LAST);
 
-       if (razor_run_script(root, RAZOR_PROPERTY_POSTUN, program, script,
-                            install_count))
-               retval = -1;
+               razor_run_script(root, RAZOR_PROPERTY_POSTUN, program, script,
+                                install_count);
+       }
 
-       environment_unset(&env);
-       environment_release(&env);
+       if (stage & RAZOR_STAGE_SCRIPTS) {
+               environment_unset(&env);
+               environment_release(&env);
+       }
 
-       return retval;
+       return razor_atomic_in_error_state(atomic);
 }
 
 RAZOR_EXPORT const char *
@@ -887,7 +964,7 @@ struct razor_install_iterator {
        struct razor_set *set;
        struct razor_set *next;
        struct array actions;
-       struct deque *order;
+       struct deque *order, *left;
 };
 
 static void
@@ -989,6 +1066,7 @@ razor_set_create_install_iterator(struct razor_set *set,
        }
 
        ii->order = graph_sort(&follows);
+       ii->left = deque_dup(ii->order);
        graph_release(&follows);
 
        return ii;
@@ -1005,10 +1083,10 @@ razor_install_iterator_next(struct razor_install_iterator *ii,
        struct razor_package *pkg;
        const char *removing, *name;
 
-       if (deque_empty(ii->order))
+       if (deque_empty(ii->left))
                return 0;
 
-       a = (struct install_action *)ii->actions.data + deque_pop(ii->order);
+       a = (struct install_action *)ii->actions.data + deque_pop(ii->left);
        *package = a->package;
        *action = a->action;
        *count = 0;
@@ -1026,15 +1104,24 @@ razor_install_iterator_next(struct razor_install_iterator *ii,
                                (*count)++;
                }
                razor_package_iterator_destroy(pi);
-       }
+       } else
+               *count = 1;
 
        return 1;
 }
 
 RAZOR_EXPORT void
+razor_install_iterator_rewind(struct razor_install_iterator *ii)
+{
+       deque_free(ii->left);
+       ii->left=deque_dup(ii->order);
+}
+
+RAZOR_EXPORT void
 razor_install_iterator_destroy(struct razor_install_iterator *ii)
 {
        array_release(&ii->actions);
        deque_free(ii->order);
+       deque_free(ii->left);
        free(ii);
 }
index 49a24b0..0806091 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
  * Copyright (C) 2008  Red Hat, Inc
- * Copyright (C) 2009  J. Ali Harlow <ali@juiblex.co.uk>
+ * Copyright (C) 2009, 2011  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
 #define _RAZOR_H_
 
 #include <stdint.h>
+#include <sys/types.h>
+
+/* GCC extensions */
+#if defined(__GNUC__)
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
+#define RAZOR_MALLOC __attribute__((__malloc__))
+#else
+#define RAZOR_MALLOC
+#endif
+#if __GNUC__ >= 4
+#define RAZOR_NULL_TERMINATED __attribute__ ((__sentinel__))
+#else
+#define RAZOR_NULL_TERMINATED
+#endif
+#endif /* __GNUC__ */
 
 enum razor_section_type {
        RAZOR_SECTION_MAIN = 0x01,
@@ -30,6 +45,14 @@ enum razor_section_type {
        RAZOR_SECTION_ALL = 0x07
 };
 
+enum razor_stage_type {
+       RAZOR_STAGE_SCRIPTS_PRE = 0x1,
+       RAZOR_STAGE_FILES = 0x2,
+       RAZOR_STAGE_SCRIPTS_POST = 0x4,
+       RAZOR_STAGE_SCRIPTS = 0x5,
+       RAZOR_STAGE_ALL = 0x7
+};
+
 enum razor_detail_type {
        RAZOR_DETAIL_LAST = 0,  /* the sentinel */
        RAZOR_DETAIL_NAME,
@@ -73,6 +96,85 @@ enum razor_property_flags {
 };
 
 /**
+ * SECTION:atomic
+ * @title: Atomic transactions
+ * @short_description: File-based transactions that shouldn't half-succeed
+ *
+ * This is a helper object for issuing a sequence of file-based actions
+ * 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.
+ **/
+struct razor_atomic;
+
+struct razor_atomic *razor_atomic_open(const char *description);
+int razor_atomic_commit(struct razor_atomic *atomic);
+const char *razor_atomic_get_error_msg(struct razor_atomic *atomic);
+void razor_atomic_destroy(struct razor_atomic *atomic);
+
+/**
+ * razor_atomic_make_dirs
+ * 
+ * Create all sub-directories leading up to path. We know root exists
+ * and is a dir, root does not end in a '/', and path either has a
+ * leading '/' or (on MS-Windows only) root is the empty string
+ * and path starts with drive (eg., "c:/windows").
+ * Note: path itself is not created, only the directory in which it
+ * (would) exist.
+ *
+ * Returns: non-zero on error.
+ **/
+int razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
+  const char *path);
+int razor_atomic_remove(struct razor_atomic *atomic, const char *path);
+int razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
+                            const char *newpath);
+
+/**
+ * razor_atomic_create_dir
+ * 
+ * Create a directory, replacing any existing object at this path except
+ * an existing directory (which is not counted as a error).
+ *
+ * Returns: non-zero on error.
+ */
+int razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
+  mode_t mode);
+
+/**
+ * razor_atomic_create_symlink
+ * 
+ * Create a symbolic link, replacing any existing object at this path except
+ * a non-empty directory.
+ *
+ * Note: This function will always fail on platforms that don't support
+ * symbolic links.
+ *
+ * Returns: non-zero on error.
+ */
+int razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
+  const char *path);
+/**
+ * razor_atomic_create_file
+ * 
+ * Create a file, replacing any existing object at this path except
+ * a non-empty directory.
+ *
+ * Returns: A handle to be passed to razor_atomic_write() and
+ * razor_atomic_close() or a negative value on error.
+ */
+int razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
+  mode_t mode);
+int razor_atomic_write(struct razor_atomic *atomic, int handle,
+  const void *data, size_t size);
+int razor_atomic_close(struct razor_atomic *atomic, int handle);
+int razor_atomic_sync(struct razor_atomic *atomic, int handle);
+void razor_atomic_abort(struct razor_atomic *atomic, const char *error_msg);
+int razor_atomic_in_error_state(struct razor_atomic *atomic);
+
+/**
  * SECTION:set
  * @title: Package Set
  * @short_description: Represents a set of packages and their metadata.
@@ -85,6 +187,9 @@ struct razor_set;
 struct razor_package;
 struct razor_property;
 
+#define RAZOR_HEADER_VERSION           2       /* Current version */
+#define RAZOR_HEADER_VERSION_MIN       1       /* Minimum version we support */
+
 /**
  * razor_set_create:
  * 
@@ -94,13 +199,20 @@ struct razor_property;
  **/
 struct razor_set *razor_set_create_without_root(void);
 struct razor_set *razor_set_create(void);
-struct razor_set *razor_set_open(const char *filename);
-void razor_set_destroy(struct razor_set *set);
-int razor_set_write_to_fd(struct razor_set *set,
-                         int fd, uint32_t section_mask);
-int razor_set_write(struct razor_set *set,
+struct razor_set *razor_set_open(const char *filename,
+                                struct razor_atomic *atomic);
+uint32_t razor_set_get_header_version(struct razor_set *set);
+int razor_set_set_header_version(struct razor_set *set,
+                                uint32_t header_version);
+void razor_set_unref(struct razor_set *set);
+struct razor_set *razor_set_ref(struct razor_set *set);
+void razor_set_write_to_handle(struct razor_set *set,
+                              struct razor_atomic *atomic, int handle,
+                              uint32_t section_mask);
+int razor_set_write(struct razor_set *set, struct razor_atomic *atomic,
                    const char *filename, uint32_t setions);
-int razor_set_bind_sections(struct razor_set *set, const char *filename);
+int razor_set_bind_sections(struct razor_set *set, struct razor_atomic *atomic,
+                           const char *filename);
 
 struct razor_package *
 razor_set_get_package(struct razor_set *set, const char *package);
@@ -110,8 +222,9 @@ razor_package_get_details(struct razor_set *set,
                          struct razor_package *package, ...);
 int
 razor_package_remove(struct razor_set *prev, struct razor_set *next,
-                    struct razor_package *package, const char *root,
-                    int install_count);
+                    struct razor_atomic *atomic, struct razor_package *package,
+                    const char *root, int install_count,
+                    enum razor_stage_type stage);
 
 /**
  * SECTION:iterator
@@ -230,6 +343,7 @@ int razor_install_iterator_next(struct razor_install_iterator *ii,
                                enum razor_install_action *action,
                                int *count);
 
+void razor_install_iterator_rewind(struct razor_install_iterator *ii);
 void razor_install_iterator_destroy(struct razor_install_iterator *ii);
 
 /**
@@ -286,12 +400,14 @@ const char *razor_relocations_apply(struct razor_relocations *relocations,
                                    const char *path);
 void razor_relocations_destroy(struct razor_relocations *relocations);
 
-struct razor_rpm *razor_rpm_open(const char *filename);
+struct razor_rpm *razor_rpm_open(const char *filename,
+                                struct razor_atomic *atomic);
 void razor_rpm_get_details(struct razor_rpm *rpm, ...);
 void razor_rpm_set_relocations(struct razor_rpm *rpm,
                               struct razor_relocations *relocations);
-int razor_rpm_install(struct razor_rpm *rpm, const char *root,
-                     int install_count);
+int razor_rpm_install(struct razor_rpm *rpm, struct razor_atomic *atomic,
+                     const char *root, int install_count,
+                     enum razor_stage_type stage);
 int razor_rpm_close(struct razor_rpm *rpm);
 
 /**
@@ -378,14 +494,15 @@ struct razor_set *razor_set_create_from_rpmdb(void);
 struct razor_root;
 
 int razor_root_create(const char *root);
-struct razor_root *razor_root_open(const char *root);
-struct razor_set *razor_root_open_read_only(const char *root);
+struct razor_root *
+razor_root_open(const char *root, struct razor_atomic *atomic);
+struct razor_set *razor_root_open_read_only(const char *root,
+                                           struct razor_atomic *atomic);
 struct razor_set *razor_root_get_system_set(struct razor_root *root);
 int razor_root_close(struct razor_root *root);
 void razor_root_update(struct razor_root *root, struct razor_set *next);
 int razor_root_commit(struct razor_root *root);
 
-
 /**
  * SECTION:misc
  * @title: Miscellaneous Functions
@@ -408,5 +525,8 @@ void razor_disable_root_name_checks(int disable);
 void razor_set_lua_loader(const char *modname, void (*loader)());
 void (*razor_get_lua_loader(const char *modname))();
 
+char *razor_concat(const char *s, ...) RAZOR_MALLOC RAZOR_NULL_TERMINATED;
+
+const char *razor_system_arch(void);
 
 #endif /* _RAZOR_H_ */
index 3ca58cf..edac683 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
  * Copyright (C) 2008  Red Hat, Inc
- * Copyright (C) 2009  J. Ali Harlow <ali@juiblex.co.uk>
+ * Copyright (C) 2009, 2011  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
@@ -60,9 +60,9 @@ static const char *razor_root_path = RAZOR_ROOT_PATH;
 struct razor_root {
        struct razor_set *system;
        struct razor_set *next;
-       int fd;
-       char path[PATH_MAX];
-       char new_path[PATH_MAX];
+       struct razor_atomic *atomic;
+       int handle;
+       char *path, *new_path;
 };
 
 static void
@@ -83,9 +83,11 @@ razor_root_init(void)
 RAZOR_EXPORT int
 razor_root_create(const char *root)
 {
+       int retval;
        struct stat buf;
        struct razor_set *set;
-       char path[PATH_MAX];
+       struct razor_atomic *atomic;
+       char *path;
 
        assert (root != NULL);
 
@@ -107,77 +109,90 @@ razor_root_create(const char *root)
                return -1;
        }
 
-       snprintf(path, sizeof path, "%s/%s",
-                razor_root_path, system_repo_filename);
-       if (razor_create_dir(root, path) < 0) {
-               fprintf(stderr, "could not create %s%s\n",
-                       root, razor_root_path);
-               return -1;
-       }
-
-       set = razor_set_create();
-       snprintf(path, sizeof path, "%s%s/%s",
-                root, razor_root_path, system_repo_filename);
-       if (stat(path, &buf) == 0) {
+       path = razor_concat(root, razor_root_path, "/", system_repo_filename,
+                           NULL);
+       retval = !stat(path, &buf);
+       free(path);
+       if (retval) {
                fprintf(stderr,
                        "a razor install root is already initialized\n");
-               return -1;
+               return retval;
        }
-       if (razor_set_write(set, path, RAZOR_SECTION_ALL) < 0) {
+
+       atomic = razor_atomic_open("Create initial package set");
+       path = razor_concat(razor_root_path, "/", system_repo_filename, NULL);
+       razor_atomic_make_dirs(atomic, root, path);
+       set = razor_set_create();
+       razor_set_write(set, atomic, path, RAZOR_SECTION_ALL);
+       free(path);
+       retval = razor_atomic_commit(atomic);
+       if (retval)
                fprintf(stderr, "could not write initial package set\n");
-               return -1;
-       }
-       razor_set_destroy(set);
+       razor_set_unref(set);
+       razor_atomic_destroy(atomic);
 
-       return 0;
+       return retval;
 }
 
 RAZOR_EXPORT struct razor_root *
-razor_root_open(const char *root)
+razor_root_open(const char *root, struct razor_atomic *atomic)
 {
        struct razor_root *image;
-       char lock_path[PATH_MAX];
+       char *lock_path;
+       int r;
 
        assert (root != NULL);
 
        razor_root_init();
        image = malloc(sizeof *image);
-       if (image == NULL)
+       if (image == NULL) {
+               razor_atomic_abort(atomic, "Not enough memory");
                return NULL;
+       }
+
+       image->atomic = atomic;
 
        image->system = razor_set_create_without_root();
        if (image->system == NULL) {
                free(image);
+               razor_atomic_abort(atomic, "Not enough memory");
                return NULL;
        }
 
-       snprintf(lock_path, sizeof lock_path,
-                "%s%s/%s", root, razor_root_path, system_lock_filename);
+       lock_path = razor_concat(root, razor_root_path, "/",
+                                system_lock_filename, NULL);
 
-       if (razor_set_aquire_lock(image->system, lock_path, 1) < 0) {
-               razor_set_destroy(image->system);
+       r = razor_set_aquire_lock(image->system, lock_path, 1);
+
+       free(lock_path);
+
+       if (r < 0) {
+               razor_atomic_abort(atomic,
+                                  "Failed to aquire exclusive system lock");
+               razor_set_unref(image->system);
                free(image);
                return NULL;
        }
 
-       snprintf(image->new_path, sizeof image->new_path,
-                "%s%s/%s", root, razor_root_path, system_tmp_filename);
-       image->fd = open(image->new_path,
-                        O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,
-                        0666);
-       if (image->fd < 0) {
-               razor_set_destroy(image->system);
+       image->new_path = razor_concat(root, razor_root_path, "/",
+                                      system_tmp_filename, NULL);
+       image->handle = razor_atomic_create_file(atomic, image->new_path,
+                                                S_IRWXU | S_IRWXG | S_IRWXO);
+       if (image->handle < 0) {
+               free(image->new_path);
+               razor_set_unref(image->system);
                free(image);
                return NULL;
        }
 
-       snprintf(image->path, sizeof image->path,
-                "%s%s/%s", root, razor_root_path, system_repo_filename);
+       image->path = razor_concat(root, razor_root_path, "/",
+                                  system_repo_filename, NULL);
 
-       if (razor_set_bind_sections(image->system, image->path)) {
-               close(image->fd);
+       if (razor_set_bind_sections(image->system, atomic, image->path)) {
                unlink(image->new_path);
-               razor_set_destroy(image->system);
+               free(image->new_path);
+               free(image->path);
+               razor_set_unref(image->system);
                free(image);
                return NULL;
        }
@@ -186,33 +201,41 @@ razor_root_open(const char *root)
 }
 
 RAZOR_EXPORT struct razor_set *
-razor_root_open_read_only(const char *root)
+razor_root_open_read_only(const char *root, struct razor_atomic *atomic)
 {
-       char path[PATH_MAX];
+       char *path;
        struct razor_set *set;
 
        assert (root != NULL);
 
        razor_root_init();
        set = razor_set_create_without_root();
-       if (set == NULL)
+       if (set == NULL) {
+               razor_atomic_abort(atomic, "Not enough memory");
                return NULL;
+       }
 
-       snprintf(path, sizeof path,
-                "%s%s/%s", root, razor_root_path, system_lock_filename);
+       path = razor_concat(root, razor_root_path, "/", system_lock_filename,
+                           NULL);
        if (razor_set_aquire_lock(set, path, 0) < 0) {
-               razor_set_destroy(set);
+               razor_atomic_abort(atomic, "Failed to aquire non-exclusive "
+                                          "system lock");
+               free(path);
+               razor_set_unref(set);
                return NULL;
        }
 
-       snprintf(path, sizeof path, "%s%s/%s",
-                root, razor_root_path, system_repo_filename);
+       free(path);
+       path = razor_concat(root, razor_root_path, "/", system_repo_filename,
+                           NULL);
 
-       if (razor_set_bind_sections(set, path)) {
-               razor_set_destroy(set);
-               return NULL;
+       if (razor_set_bind_sections(set, atomic, path)) {
+               razor_set_unref(set);
+               set = NULL;
        }
 
+       free(path);
+
        return set;
 }
 
@@ -229,9 +252,11 @@ razor_root_close(struct razor_root *root)
 {
        assert (root != NULL);
 
-       razor_set_destroy(root->system);
-       close(root->fd);
-       unlink(root->new_path);
+       razor_set_unref(root->system);
+       razor_atomic_close(root->atomic, root->handle);
+       razor_atomic_remove(root->atomic, root->new_path);
+       free(root->path);
+       free(root->new_path);
        free(root);
 
        return 0;
@@ -244,13 +269,13 @@ razor_root_update(struct razor_root *root, struct razor_set *next)
        assert (next != NULL);
 
        razor_root_init();
-       razor_set_write_to_fd(next, root->fd, RAZOR_SECTION_ALL);
+       razor_set_write_to_handle(next, root->atomic, root->handle,
+                                 RAZOR_SECTION_ALL);
        root->next = next;
 
        /* Sync the new repo file so the new package set is on disk
         * before we start upgrading. */
-       fsync(root->fd);
-       printf("wrote %s\n", root->new_path);
+       razor_atomic_sync(root->atomic, root->handle);
 }
 
 RAZOR_EXPORT int
@@ -259,18 +284,12 @@ razor_root_commit(struct razor_root *root)
        int retval;
        assert (root != NULL);
 
-       /* Make it so. */
-       close(root->fd);
-#ifdef MSWIN_API
-       /* Rename is not atomic under MS-Windows */
-       remove(root->path);
-#endif
-       retval = rename(root->new_path, root->path);
-       if (retval)
-               perror(root->path);
-       else
-               printf("renamed %s to %s\n", root->new_path, root->path);
-       razor_set_destroy(root->system);
+       razor_atomic_close(root->atomic, root->handle);
+       retval = razor_atomic_rename_file(root->atomic, root->new_path,
+                                         root->path);
+       razor_set_unref(root->system);
+       free(root->path);
+       free(root->new_path);
        free(root);
 
        return retval;
index cfcb3e8..db37119 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
  * Copyright (C) 2008  Red Hat, Inc
- * Copyright (C) 2009  J. Ali Harlow <ali@juiblex.co.uk>
+ * Copyright (C) 2009, 2011  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
@@ -602,24 +602,29 @@ razor_rpm_get_details(struct razor_rpm *rpm, ...)
 }
 
 RAZOR_EXPORT struct razor_rpm *
-razor_rpm_open(const char *filename)
+razor_rpm_open(const char *filename, struct razor_atomic *atomic)
 {
        struct razor_rpm *rpm;
        struct rpm_header_index *base, *index;
        unsigned int count, i, nindex, hsize;
        const char *name, *prefix;
+       char *s;
 
        assert (filename != NULL);
 
-       rpm = malloc(sizeof *rpm);
-       if (rpm == NULL)
+       rpm = zalloc(sizeof *rpm);
+       if (rpm == NULL) {
+               razor_atomic_abort(atomic, "Not enough memory");
                return NULL;
+       }
        memset(rpm, 0, sizeof *rpm);
 
        rpm->map = razor_file_get_contents(filename, &rpm->size);
        if (!rpm->map) {
-               fprintf(stderr, "couldn't get contents of %s (%s)\n", filename,
-                       strerror(errno));
+               s = razor_concat(filename, ": ", strerror(errno), NULL);
+               razor_atomic_abort(atomic, s);
+               free(s);
+               free(rpm);
                return NULL;
        }
 
@@ -648,7 +653,11 @@ razor_rpm_open(const char *filename)
                name = razor_rpm_get_indirect(rpm, RPMTAG_OLDFILENAMES,
                                              &count);
                if (name) {
-                       fprintf(stderr, "old filenames not supported\n");
+                       razor_rpm_close(rpm);
+                       s = razor_concat(filename,
+                                        ": Old filenames not supported", NULL);
+                       razor_atomic_abort(atomic, s);
+                       free(s);
                        return NULL;
                }
        }
@@ -666,7 +675,12 @@ razor_rpm_open(const char *filename)
                prefix = razor_rpm_get_indirect(rpm, RPMTAG_DEFAULTPREFIX,
                                                &count);
                if (prefix) {
-                       fprintf(stderr, "default prefix not supported\n");
+                       razor_rpm_close(rpm);
+                       s = razor_concat(filename,
+                                        ": Default prefix not supported",
+                                        NULL);
+                       razor_atomic_abort(atomic, s);
+                       free(s);
                        return NULL;
                }
        }
@@ -711,6 +725,7 @@ struct cpio_file_header {
 struct installer {
        const char *root;
        struct razor_rpm *rpm;
+       struct razor_atomic *atomic;
        z_stream stream;
        unsigned char buffer[32768];
        size_t rest, length;
@@ -731,8 +746,7 @@ installer_inflate(struct installer *installer)
        installer->stream.avail_out = length;
        err = inflate(&installer->stream, Z_SYNC_FLUSH);
        if (err != Z_OK && err != Z_STREAM_END) {
-               fprintf(stderr, "inflate error: %d (%s)\n", err,
-                       strerror(errno));
+               razor_atomic_abort(installer->atomic, "Failed to inflate");
                return -1;
        }
 
@@ -757,8 +771,7 @@ installer_align(struct installer *installer, size_t size)
 
        err = inflate(&installer->stream, Z_SYNC_FLUSH);
        if (err != Z_OK && err != Z_STREAM_END) {
-               fprintf(stderr, "inflate error: %d (%s)\n", err,
-                       strerror(errno));
+               razor_atomic_abort(installer->atomic, "Failed to inflate");
                return -1;
        }
 
@@ -768,80 +781,74 @@ installer_align(struct installer *installer, size_t size)
 static int
 create_path(struct installer *installer, const char *path, unsigned int mode)
 {
-       char buffer[PATH_MAX];
-       struct stat buf;
-       int fd, ret;
+       char *s, *buffer;
+       int h, ret;
 
-       if (razor_create_dir(installer->root, path) < 0)
+       if (razor_atomic_make_dirs(installer->atomic, installer->root, path))
                return -1;
 
-       snprintf(buffer, sizeof buffer, "%s%s", installer->root, path);
+       buffer = razor_concat(installer->root, path, NULL);
 
        switch (mode >> 12) {
        case REG:
                /* FIXME: handle the case where a file is already there. */
-               fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
-                         mode & 0x1ff);
-               if (fd < 0){
-                       fprintf(stderr, "failed to create file %s\n", buffer);
+               h = razor_atomic_create_file(installer->atomic, buffer, mode);
+               free(buffer);
+               if (h < 0)
                        return -1;
-               }
                while (installer->rest > 0) {
-                       if (installer_inflate(installer)) {
-                               fprintf(stderr, "failed to inflate\n");
+                       if (installer_inflate(installer))
                                return -1;
-                       }
-                       if (razor_write(fd, installer->buffer,
-                                       installer->length)) {
-                               fprintf(stderr, "failed to write payload\n");
+                       if (razor_atomic_write(installer->atomic, h,
+                                              installer->buffer,
+                                              installer->length))
                                return -1;
-                       }
                }
-               if (close(fd) < 0) {
-                       fprintf(stderr, "failed to close %s: %s\n", buffer,
-                               strerror(errno));
-                       return -1;
-               }
-               return 0;
+               return razor_atomic_close(installer->atomic, h);
        case XDIR:
-               ret = mkdir(buffer, mode & 0x1ff);
-               if (ret == 0 || errno != EEXIST)
-                       return ret;
-               if (stat(buffer, &buf) || !S_ISDIR(buf.st_mode)) {
-                       /* FIXME: also check that mode match. */
-                       fprintf(stderr,
-                               "%s exists but is not a directory\n", buffer);
-                       return -1;
-               }
-               return 0;
+               ret = razor_atomic_create_dir(installer->atomic, buffer, mode);
+               free(buffer);
+               return ret;
        case LINK:
 #if HAVE_SYMLINK
-               if (installer_inflate(installer)) {
-                       fprintf(stderr, "failed to inflate\n");
+               if (installer_inflate(installer))
                        return -1;
-               }
                if (installer->length >= sizeof installer->buffer) {
-                       fprintf(stderr, "link name too long\n");
+                       razor_atomic_abort(installer->atomic,
+                         "Link target too long");
                        return -1;
                }
                installer->buffer[installer->length] = '\0';
-               if (symlink((const char *) installer->buffer, buffer)) {
-                       perror("failed to create symlink");
-                       return -1;
-               }
-               return 0;
+               ret = razor_atomic_create_symlink(installer->atomic,
+                 (const char *)installer->buffer, buffer);
+               free(buffer);
+               return ret;
 #else
-               /* fall through */
+               s = "Symbolic links";
+               goto unsupported;
 #endif
        case PIPE:
+               s = "Named pipes";
+unsupported:
+               free(buffer);
+               buffer = razor_concat(s, " are not supported on this platform",
+                 NULL);
+               razor_atomic_abort(installer->atomic, buffer);
+               free(buffer);
+               return -1;
        case CDEV:
+               s = "Character devices";
+               goto unsupported;
        case BDEV:
+               s = "Block devices";
+               goto unsupported;
        case SOCK:
-               printf("%s: unhandled file type %d\n", buffer, mode >> 12);
-               return 0;
+               s = "Named sockets";
+               goto unsupported;
        default:
-               printf("%s: unknown file type %d\n", buffer, mode >> 12);
-               return 0;
+               free(buffer);
+               razor_atomic_abort(installer->atomic, "Unknown file type");
+               return -1;
        }
 }
 
@@ -1046,10 +1053,12 @@ installer_init(struct installer *installer)
 {
        unsigned char *gz_header;
        int method, flags, err;
+       char buffer[32], *s;
 
        gz_header = installer->rpm->payload;
        if (gz_header[0] != 0x1f || gz_header[1] != 0x8b) {
-               fprintf(stderr, "payload section doesn't have gz header\n");
+               razor_atomic_abort(installer->atomic, 
+                                  "Payload section doesn't have gz header");
                return -1;
        }
 
@@ -1057,8 +1066,9 @@ installer_init(struct installer *installer)
        flags = gz_header[3];
 
        if (method != Z_DEFLATED || flags != 0) {
-               fprintf(stderr,
-                       "unknown payload compression method or flags set\n");
+               razor_atomic_abort(installer->atomic, 
+                                  "Unknown payload compression method or "
+                                  "flags set");
                return -1;
        }
 
@@ -1075,7 +1085,10 @@ installer_init(struct installer *installer)
 
        err = inflateInit2(&installer->stream, -MAX_WBITS);
        if (err != Z_OK) {
-               fprintf(stderr, "inflateInit error: %d\n", err);
+               sprintf(buffer, "%d", err);
+               s = razor_concat("inflateEnd error: ", s, NULL);
+               razor_atomic_abort(installer->atomic, s);
+               free(s);
                return -1;
        }
 
@@ -1086,15 +1099,18 @@ static int
 installer_finish(struct installer *installer)
 {
        int err;
+       char buffer[32], *s;
 
        err = inflateEnd(&installer->stream);
 
        if (err != Z_OK) {
-               fprintf(stderr, "inflateEnd error: %d\n", err);
-               return -1;
+               sprintf(buffer, "%d", err);
+               s = razor_concat("inflateEnd error: ", s, NULL);
+               razor_atomic_abort(installer->atomic, s);
+               free(s);
        }
 
-       return 0;
+       return razor_atomic_in_error_state(installer->atomic);
 }
 
 static unsigned long
@@ -1114,73 +1130,86 @@ fixed_hex_to_ulong(const char *hex, int length)
 }
 
 RAZOR_EXPORT int
-razor_rpm_install(struct razor_rpm *rpm, const char *root, int install_count)
+razor_rpm_install(struct razor_rpm *rpm, struct razor_atomic *atomic,
+                 const char *root, int install_count,
+                 enum razor_stage_type stage)
 {
        struct installer installer;
        struct cpio_file_header *header;
        struct stat buf;
        unsigned int mode;
-       const char *path;
+       const char *path, *name;
        size_t filesize;
+       char *s;
 
        assert (rpm != NULL);
        assert (root != NULL);
 
        installer.rpm = rpm;
        installer.root = root;
+       installer.atomic = atomic;
 
        /* FIXME: Only do this before a transaction, not per rpm. */
        if (*root && (stat(root, &buf) < 0 || !S_ISDIR(buf.st_mode))) {
-               fprintf(stderr,
-                       "root installation directory \"%s\" does not exist\n",
-                       root);
+               s = razor_concat(root, ": Directory does not exist", NULL);
+               razor_atomic_abort(stderr, s);
+               free(s);
                return -1;
        }
 
        if (rpm->relocations)
                razor_relocations_set_rpm(rpm->relocations, rpm);
 
-       if (installer_init(&installer))
-               return -1;
-
-       run_script(&installer, RPMTAG_PREINPROG, RPMTAG_PREIN, install_count);
+       if (stage & RAZOR_STAGE_SCRIPTS_PRE)
+               run_script(&installer, RPMTAG_PREINPROG, RPMTAG_PREIN,
+                          install_count);
 
-       while (installer.stream.avail_in > 0) {
-               installer.rest = sizeof *header;
-               if (installer_inflate(&installer))
+       if (stage & RAZOR_STAGE_FILES) {
+               if (installer_init(&installer))
                        return -1;
 
-               header = (struct cpio_file_header *) installer.buffer;
-               mode = fixed_hex_to_ulong(header->mode, sizeof header->mode);
-               filesize = fixed_hex_to_ulong(header->filesize,
-                                             sizeof header->filesize);
+               while (installer.stream.avail_in > 0) {
+                       installer.rest = sizeof *header;
+                       if (installer_inflate(&installer))
+                               break;
 
-               installer.rest = fixed_hex_to_ulong(header->namesize,
-                                                   sizeof header->namesize);
+                       header = (struct cpio_file_header *) installer.buffer;
+                       mode = fixed_hex_to_ulong(header->mode,
+                                                 sizeof header->mode);
+                       filesize = fixed_hex_to_ulong(header->filesize,
+                                                     sizeof header->filesize);
 
-               if (installer_inflate(&installer) ||
-                   installer_align(&installer, 4))
-                       return -1;
+                       installer.rest =
+                         fixed_hex_to_ulong(header->namesize,
+                                            sizeof header->namesize);
 
-               path = (const char *) installer.buffer;
-               /* This convention is so lame... */
-               if (strcmp(path, "TRAILER!!!") == 0)
-                       break;
+                       if (installer_inflate(&installer) ||
+                           installer_align(&installer, 4))
+                               break;
 
-               installer.rest = filesize;
-               path++;
-               if (rpm->relocations)
-                       path = razor_relocations_apply(rpm->relocations, path);
-               if (create_path(&installer, path, mode) < 0)
-                       return -1;
-               if (installer_align(&installer, 4))
+                       path = (const char *) installer.buffer;
+                       /* This convention is so lame... */
+                       if (strcmp(path, "TRAILER!!!") == 0)
+                               break;
+
+                       installer.rest = filesize;
+                       path++;
+                       if (rpm->relocations)
+                               path = razor_relocations_apply(rpm->relocations,
+                                                              path);
+                       if (create_path(&installer, path, mode))
+                               break;
+                       if (installer_align(&installer, 4))
+                               break;
+               }
+
+               if (installer_finish(&installer))
                        return -1;
        }
 
-       if (installer_finish(&installer))
-               return -1;
-
-       run_script(&installer, RPMTAG_POSTINPROG, RPMTAG_POSTIN, install_count);
+       if (stage & RAZOR_STAGE_SCRIPTS_POST)
+               run_script(&installer, RPMTAG_POSTINPROG, RPMTAG_POSTIN,
+                          install_count);
 
        return 0;
 }
index 23c4a2e..13d6a9a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
  * Copyright (C) 2008  Red Hat, Inc
- * Copyright (C) 2009  J. Ali Harlow <ali@juiblex.co.uk>
+ * Copyright (C) 2009, 2011  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
@@ -109,7 +109,7 @@ transaction_set_init(struct transaction_set *ts, struct razor_set *set)
 {
        int count;
 
-       ts->set = set;
+       ts->set = razor_set_ref(set);
        count = set->packages.size / sizeof (struct razor_package);
        ts->packages = zalloc(count * sizeof *ts->packages);
        count = set->properties.size / sizeof (struct razor_property);
@@ -119,6 +119,7 @@ transaction_set_init(struct transaction_set *ts, struct razor_set *set)
 static void
 transaction_set_release(struct transaction_set *ts)
 {
+       razor_set_unref(ts->set);
        free(ts->packages);
        free(ts->properties);
 }
@@ -321,7 +322,9 @@ remove_matching_providers(struct razor_transaction *trans,
                                                   RAZOR_DETAIL_NAME, &n,
                                                   RAZOR_DETAIL_VERSION, &v,
                                                   RAZOR_DETAIL_LAST)) {
+#if 0
                        fprintf(stderr, "removing %s-%s\n", n, v);
+#endif
                        razor_transaction_remove_package(trans, pkg);
                }
        }
@@ -369,11 +372,13 @@ flag_matching_providers(struct razor_transaction *trans,
                                                   RAZOR_DETAIL_VERSION, &version,
                                                   RAZOR_DETAIL_LAST)) {
 
+#if 0
                        fprintf(stderr, "flagging %s-%s for providing %s matching %s %s\n",
                                name, version,
                                ppi->pool + p->name,
                                rpi->pool + r->name,
                                rpi->pool + r->version);
+#endif
                        flags[pkg - pkgs] |= flag;
                }
        }
@@ -542,11 +547,13 @@ update_unsatisfied_packages(struct razor_transaction *trans)
                while (razor_package_iterator_next(&pkg_iter, &pkg,
                                                   RAZOR_DETAIL_NAME, &name,
                                                   RAZOR_DETAIL_LAST)) {
+#if 0
                        fprintf(stderr, "updating %s because %s %s %s "
                                "isn't satisfied\n",
                                name, spi.pool + sp->name,
                                razor_property_relation_to_string(sp),
                                spi.pool + sp->version);
+#endif
                        trans->system.packages[pkg - spkgs] |=
                                TRANS_PACKAGE_UPDATE;
                }
@@ -595,9 +602,11 @@ update_conflicted_packages(struct razor_transaction *trans)
                                                   RAZOR_DETAIL_NAME, &name,
                                                   RAZOR_DETAIL_VERSION, &version,
                                                   RAZOR_DETAIL_LAST)) {
+#if 0
                        fprintf(stderr, "updating %s %s because it "
                                "conflicts with %s\n",
                                name, version, spi.pool + sp->name);
+#endif
                        trans->system.packages[pkg - spkgs] |=
                                TRANS_PACKAGE_UPDATE;
                }
@@ -640,6 +649,7 @@ pull_in_requirements(struct razor_transaction *trans,
 
                rpi->present[rp - rpi->start] |= TRANS_PROPERTY_SATISFIED;
 
+#if 0
                fprintf(stderr, "pulling in %s-%s.%s which provides %s %s %s "
                        "to satisfy %s %s %s\n",
                        ppi->pool + pkg->name,
@@ -651,6 +661,7 @@ pull_in_requirements(struct razor_transaction *trans,
                        &rpi->pool[rp->name],
                        razor_property_relation_to_string(rp),
                        &rpi->pool[rp->version]);
+#endif
 
                trans->upstream.packages[pkg - upkgs] |= TRANS_PACKAGE_UPDATE;
        }
@@ -736,7 +747,9 @@ flush_scheduled_upstream_updates(struct razor_transaction *trans)
                                                  RAZOR_PROPERTY_LESS,
                                                  version);
                razor_transaction_install_package(trans, p);
+#if 0
                fprintf(stderr, "installing %s-%s\n", name, version);
+#endif
        }
 }
 
index 888e22c..584c260 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
  * Copyright (C) 2008  Red Hat, Inc
- * Copyright (C) 2009  J. Ali Harlow <ali@juiblex.co.uk>
+ * Copyright (C) 2009, 2011  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
@@ -33,6 +33,8 @@
 #ifdef MSWIN_API
 #include <windows.h>
 #include <direct.h>
+#else
+#include <sys/utsname.h>
 #endif
 #if HAVE_SYS_MMAN_H
 #include <sys/mman.h>
 #define O_BINARY       0
 #endif
 
-#define RAZOR_ASCII_ISALPHA(c) \
-                       ((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z')
-
 /* Required by gnulib on non-libc platforms */
 char *program_name = "librazor";
 
-static int allow_all_root_names = 0;
-
-/*
- * Primarily intended for testing named roots under UNIX platforms.
- */
-RAZOR_EXPORT void razor_disable_root_name_checks(int disable)
-{
-       allow_all_root_names = disable;
-}
-
-static int razor_valid_root_name(const char *name)
-{
-       if (allow_all_root_names)
-               return !strchr(name,'/');
-
-#ifdef MSWIN_API
-       return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
-              name[2] == '\0';
-#else
-       return name[0] == '\0';
-#endif
-}
-
-int
-razor_create_dir(const char *root, const char *path)
-{
-       char buffer[PATH_MAX], *p;
-       const char *slash, *next;
-       struct stat buf;
-
-       /* Create all sub-directories in dir. We know root exists and
-        * is a dir, root does not end in a '/', and path either has a
-        * leading '/' or (on MS-Windows only) root is the empty string
-        * and path starts with drive (eg., "c:/windows"). */
-
-       strcpy(buffer, root);
-       p = buffer + strlen(buffer);
-       slash = path;
-       for (slash = path; *slash != '\0'; slash = next) {
-               next = strchr(slash + 1, '/');
-               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)) {
-                               fprintf(stderr,
-                                       "%s exists but is not a directory\n",
-                                       buffer);
-                               return -1;
-                       }
-               } else if (mkdir(buffer, 0777) < 0) {
-                       fprintf(stderr, "failed to make directory %s: %s\n",
-                               buffer, strerror(errno));
-                       return -1;
-               }
-
-               /* FIXME: What to do about permissions for dirs we
-                * have to create but are not in the cpio archive? */
-       }
-
-       return 0;
-}
-
-int
-razor_remove(const char *path)
-{
-#ifdef MSWIN_API
-       DWORD err;
-
-       if (DeleteFile(path))
-               return 0;
-
-       err = GetLastError();
-       if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
-               return 0;
-
-       if (SetFileAttributes(path, FILE_ATTRIBUTE_NORMAL) && DeleteFile(path))
-               return 0;
-
-       if (RemoveDirectory(path) || GetLastError() == ERROR_DIR_NOT_EMPTY)
-               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.
-        */
-
-       /* Use remove() as a fallback so that errno is set appropriately */
-#endif
-
-       return remove(path);
-}
-
-int
-razor_write(int fd, const void *data, size_t size)
-{
-       size_t rest;
-       ssize_t written;
-       const unsigned char *p;
-
-       rest = size;
-       p = data;
-       while (rest > 0) {
-               written = write(fd, p, rest);
-               if (written < 0) {
-                       perror("write error");
-                       return -1;
-               }
-               rest -= written;
-               p += written;
-       }
-
-       return 0;
-}
-
 void *
 razor_file_get_contents(const char *filename, size_t *length)
 {
@@ -225,7 +99,6 @@ razor_file_get_contents(const char *filename, size_t *length)
        return addr;
 }
 
-int
 razor_file_free_contents(void *addr, size_t length)
 {
 #if HAVE_SYS_MMAN_H
@@ -396,3 +269,66 @@ void environment_release(struct environment *env)
        array_release(&env->string_pool);
        array_release(&env->vars);
 }
+
+RAZOR_EXPORT char *razor_concat(const char *s, ...)
+{
+       va_list args;
+       const char *string;
+       char *concat;
+       size_t n, len;
+
+       va_start(args, s);
+
+       len = strlen(s);
+       while((string = va_arg(args, const char *)))
+               len += strlen(string);
+
+       va_end(args);
+
+       concat = malloc(len + 1);
+
+       if (!concat)
+               return NULL;
+
+       va_start(args, s);
+
+       len = strlen(s);
+       memcpy(concat, s, len);
+       n = len;
+       while((string = va_arg(args, const char *))) {
+               len = strlen(string);
+               memcpy(concat + n, string, len);
+               n += len;
+       }
+
+       va_end(args);
+
+       concat[n] = '\0';
+
+       return concat;
+}
+
+RAZOR_EXPORT const char *razor_system_arch(void)
+{
+#ifdef MSWIN_API
+       SYSTEM_INFO si;
+
+       GetNativeSystemInfo(&si);
+       switch(si.wProcessorArchitecture)
+       {
+               case PROCESSOR_ARCHITECTURE_INTEL:
+                       return "i686";
+               case PROCESSOR_ARCHITECTURE_AMD64:
+                       return "x86_64";
+               default:
+                       return NULL;
+       }
+#else
+       static struct utsname un;
+
+       if (uname(&un))
+               return NULL;
+       else
+               return un.machine;
+#endif
+}
index 8d5c097..42330de 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
  * Copyright (C) 2008  Red Hat, Inc
- * Copyright (C) 2009  J. Ali Harlow <ali@juiblex.co.uk>
+ * Copyright (C) 2009, 2011  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
@@ -49,8 +49,11 @@ static const char *yum_url;
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
 
 static int
-update_packages(struct razor_transaction *trans, struct razor_set *system,
-               struct razor_set *next, struct razor_relocations *relocations);
+update_packages(struct razor_transaction *trans,
+               struct razor_install_iterator *ii, struct razor_set *system,
+               struct razor_set *next, struct razor_atomic *atomic,
+               struct razor_relocations *relocations,
+               enum razor_stage_type stage);
 
 static struct razor_package_iterator *
 create_iterator_from_argv(struct razor_set *set, int argc, const char *argv[])
@@ -113,6 +116,7 @@ static int
 command_list(int argc, const char *argv[])
 {
        struct razor_package_iterator *pi;
+       struct razor_atomic *atomic;
        struct razor_set *set;
        uint32_t flags = 0;
        int i = 0;
@@ -122,14 +126,19 @@ command_list(int argc, const char *argv[])
                i++;
        }
 
-       set = razor_root_open_read_only(install_root);
-       if (set == NULL)
+       atomic = razor_atomic_open("List installed packages");
+       set = razor_root_open_read_only(install_root, atomic);
+       if (set == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
+       }
 
        pi = create_iterator_from_argv(set, argc - i, argv + i);
        list_packages(pi, flags);
        razor_package_iterator_destroy(pi);
-       razor_set_destroy(set);
+       razor_set_unref(set);
+       razor_atomic_destroy(atomic);
 
        return 0;
 }
@@ -175,13 +184,18 @@ static int
 list_properties(int argc, const char *argv[], uint32_t type)
 {
        struct razor_set *set;
+       struct razor_atomic *atomic;
        struct razor_package *package;
        struct razor_package_iterator *pi;
        const char *name, *version, *arch;
 
-       set = razor_root_open_read_only(install_root);
-       if (set == NULL)
+       atomic = razor_atomic_open("List package properties");
+       set = razor_root_open_read_only(install_root, atomic);
+       if (set == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
+       }
 
        pi = create_iterator_from_argv(set, argc, argv);
        while (razor_package_iterator_next(pi, &package,
@@ -191,7 +205,8 @@ list_properties(int argc, const char *argv[], uint32_t type)
                                           RAZOR_DETAIL_LAST))
                list_package_properties(set, package, type);
        razor_package_iterator_destroy(pi);
-       razor_set_destroy(set);
+       razor_set_unref(set);
+       razor_atomic_destroy(atomic);
 
        return 0;
 }
@@ -224,13 +239,18 @@ static int
 command_list_scripts(int argc, const char *argv[])
 {
        struct razor_set *set;
+       struct razor_atomic *atomic;
        struct razor_package *package;
        struct razor_package_iterator *pi;
        const char *preunprog, *preun, *postunprog, *postun;
 
-       set = razor_root_open_read_only(install_root);
-       if (set == NULL)
+       atomic = razor_atomic_open("List package scripts");
+       set = razor_root_open_read_only(install_root, atomic);
+       if (set == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
+       }
 
        pi = create_iterator_from_argv(set, argc, argv);
        while (razor_package_iterator_next(pi, &package,
@@ -253,7 +273,8 @@ command_list_scripts(int argc, const char *argv[])
                }
        }
        razor_package_iterator_destroy(pi);
-       razor_set_destroy(set);
+       razor_set_unref(set);
+       razor_atomic_destroy(atomic);
 
        return 0;
 }
@@ -261,14 +282,20 @@ command_list_scripts(int argc, const char *argv[])
 static int
 command_list_files(int argc, const char *argv[])
 {
+       struct razor_atomic *atomic;
        struct razor_set *set;
 
-       set = razor_root_open_read_only(install_root);
-       if (set == NULL)
+       atomic = razor_atomic_open("List package files");
+       set = razor_root_open_read_only(install_root, atomic);
+       if (set == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
+       }
 
        razor_set_list_files(set, argv[0]);
-       razor_set_destroy(set);
+       razor_set_unref(set);
+       razor_atomic_destroy(atomic);
 
        return 0;
 }
@@ -276,18 +303,24 @@ command_list_files(int argc, const char *argv[])
 static int
 command_list_file_packages(int argc, const char *argv[])
 {
+       struct razor_atomic *atomic;
        struct razor_set *set;
        struct razor_package_iterator *pi;
 
-       set = razor_root_open_read_only(install_root);
-       if (set == NULL)
+       atomic = razor_atomic_open("List file packages");
+       set = razor_root_open_read_only(install_root, atomic);
+       if (set == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
+       }
 
        pi = razor_package_iterator_create_for_file(set, argv[0]);
        list_packages(pi, 0);
        razor_package_iterator_destroy(pi);
 
-       razor_set_destroy(set);
+       razor_set_unref(set);
+       razor_atomic_destroy(atomic);
 
        return 0;
 }
@@ -295,14 +328,19 @@ command_list_file_packages(int argc, const char *argv[])
 static int
 command_list_package_files(int argc, const char *argv[])
 {
+       struct razor_atomic *atomic;
        struct razor_set *set;
        struct razor_package_iterator *pi;
        struct razor_package *package;
        const char *name, *version, *arch;
 
-       set = razor_root_open_read_only(install_root);
-       if (set == NULL)
+       atomic = razor_atomic_open("List package files");
+       set = razor_root_open_read_only(install_root, atomic);
+       if (set == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
+       }
 
        pi = create_iterator_from_argv(set, argc, argv);
        while (razor_package_iterator_next(pi, &package,
@@ -313,7 +351,8 @@ command_list_package_files(int argc, const char *argv[])
                razor_set_list_package_files(set, package);
        razor_package_iterator_destroy(pi);
 
-       razor_set_destroy(set);
+       razor_set_unref(set);
+       razor_atomic_destroy(atomic);
 
        return 0;
 }
@@ -323,6 +362,7 @@ list_property_packages(const char *ref_name,
                       const char *ref_version,
                       uint32_t type)
 {
+       struct razor_atomic *atomic;
        struct razor_set *set;
        struct razor_property *property;
        struct razor_property_iterator *prop_iter;
@@ -333,9 +373,13 @@ list_property_packages(const char *ref_name,
        if (ref_name == NULL)
                return 0;
 
-       set = razor_root_open_read_only(install_root);
-       if (set == NULL)
+       atomic = razor_atomic_open("List package properties");
+       set = razor_root_open_read_only(install_root, atomic);
+       if (set == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
+       }
 
        prop_iter = razor_property_iterator_create(set, NULL);
        while (razor_property_iterator_next(prop_iter, &property,
@@ -357,7 +401,8 @@ list_property_packages(const char *ref_name,
        }
        razor_property_iterator_destroy(prop_iter);
 
-       razor_set_destroy(set);
+       razor_set_unref(set);
+       razor_atomic_destroy(atomic);
 
        return 0;
 }
@@ -454,7 +499,9 @@ download_if_missing(const char *url, const char *file)
 static int
 command_import_yum(int argc, const char *argv[])
 {
+       int retval;
        struct razor_set *set;
+       struct razor_atomic *atomic;
        char buffer[512];
 
        printf("downloading from %s.\n", yum_url);
@@ -470,14 +517,17 @@ command_import_yum(int argc, const char *argv[])
        set = razor_set_create_from_yum();
        if (set == NULL)
                return 1;
-       if (razor_set_write(set, rawhide_repo_filename, RAZOR_SECTION_ALL)) {
-               perror(rawhide_repo_filename);
-               return -1;
-       }
-       razor_set_destroy(set);
-       printf("wrote %s\n", rawhide_repo_filename);
+       atomic = razor_atomic_open("Yum import repository");
+       razor_set_write(set, atomic, rawhide_repo_filename, RAZOR_SECTION_ALL);
+       retval = razor_atomic_commit(atomic);
+       razor_set_unref(set);
+       if (retval)
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+       else
+               printf("wrote %s\n", rawhide_repo_filename);
+       razor_atomic_destroy(atomic);
 
-       return 0;
+       return retval;
 }
 
 #if HAVE_RPMLIB
@@ -486,10 +536,17 @@ command_import_rpmdb(int argc, const char *argv[])
 {
        struct razor_set *set;
        struct razor_root *root;
+       struct razor_atomic *atomic;
+       int retval;
+
+       atomic = razor_atomic_open("Import RPM database");
 
-       root = razor_root_open(install_root);
-       if (root == NULL)
+       root = razor_root_open(install_root, atomic);
+       if (root == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
+       }
 
        set = razor_set_create_from_rpmdb();
        if (set == NULL)
@@ -497,7 +554,13 @@ command_import_rpmdb(int argc, const char *argv[])
 
        razor_root_update(root, set);
 
-       return razor_root_commit(root);
+       retval = razor_root_commit(root);
+       if (retval)
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+
+       razor_atomic_destroy(atomic);
+
+       return retval;
 }
 #endif
 
@@ -553,45 +616,69 @@ command_remove(int argc, const char *argv[])
        struct razor_root *root;
        struct razor_set *system, *upstream, *next;
        struct razor_transaction *trans;
-       int i, errors;
+       struct razor_atomic *atomic;
+       struct razor_install_iterator *ii;
+       int i, retval;
+
+       atomic = razor_atomic_open("Remove packages");
 
-       root = razor_root_open(install_root);
-       system = razor_root_get_system_set(root);
+       root = razor_root_open(install_root, atomic);
+       system = razor_set_ref(razor_root_get_system_set(root));
        if (system == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
                razor_root_close(root);
+               razor_atomic_destroy(atomic);
                return 1;
        }
 
        upstream = razor_set_create_without_root();
        trans = razor_transaction_create(system, upstream);
+       razor_set_unref(upstream);
        for (i = 0; i < argc; i++) {
                if (mark_packages_for_removal(trans, system, argv[i]) == 0) {
                        fprintf(stderr, "no match for %s\n", argv[i]);
                        razor_transaction_destroy(trans);
-                       razor_set_destroy(upstream);
+                       razor_set_unref(system);
                        razor_root_close(root);
+                       razor_atomic_destroy(atomic);
                        return 1;
                }
        }
 
        razor_transaction_resolve(trans);
-       errors = razor_transaction_describe(trans);
-       if (errors) {
+       retval = razor_transaction_describe(trans);
+       if (retval) {
                razor_transaction_destroy(trans);
-               razor_set_destroy(upstream);
+               razor_set_unref(system);
                razor_root_close(root);
+               razor_atomic_destroy(atomic);
                return 1;
        }
 
        next = razor_transaction_commit(trans);
-       update_packages(trans, system, next, NULL);
+       ii = razor_set_create_install_iterator(system, next);
+       update_packages(trans, ii, system, next, atomic, NULL,
+                       RAZOR_STAGE_SCRIPTS_PRE);
+       update_packages(trans, ii, system, next, atomic, NULL,
+                       RAZOR_STAGE_FILES);
+
        razor_root_update(root, next);
 
+       (void)razor_root_commit(root);
+       retval = razor_atomic_commit(atomic);
+       if (retval)
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+       else
+               update_packages(trans, ii, system, next, atomic, NULL,
+                               RAZOR_STAGE_SCRIPTS_POST);
+       razor_install_iterator_destroy(ii);
+
        razor_transaction_destroy(trans);
-       razor_set_destroy(next);
-       razor_set_destroy(upstream);
+       razor_atomic_destroy(atomic);
+       razor_set_unref(system);
+       razor_set_unref(next);
 
-       return razor_root_commit(root);
+       return retval;
 }
 
 static void
@@ -611,17 +698,23 @@ print_diff(enum razor_diff_action action,
 static int
 command_diff(int argc, const char *argv[])
 {
+       struct razor_atomic *atomic;
        struct razor_set *set, *updated;
 
-       set = razor_root_open_read_only(install_root);
-       updated = razor_set_open(rawhide_repo_filename);
-       if (set == NULL || updated == NULL)
+       atomic = razor_atomic_open("Show package differences");
+       set = razor_root_open_read_only(install_root, atomic);
+       updated = razor_set_open(rawhide_repo_filename, atomic);
+       if (set == NULL || updated == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
+       }
 
        razor_set_diff(set, updated, print_diff, NULL);
 
-       razor_set_destroy(set);
-       razor_set_destroy(updated);
+       razor_set_unref(set);
+       razor_set_unref(updated);
+       razor_atomic_destroy(atomic);
 
        return 0;
 }
@@ -634,9 +727,11 @@ command_import_rpms(int argc, const char *argv[])
        struct razor_importer *importer;
        struct razor_set *set;
        struct razor_rpm *rpm;
+       struct razor_atomic *atomic;
        int len, imported_count = 0;
        char filename[256];
        const char *dirname = argv[0];
+       int retval;
 
        if (dirname == NULL) {
                fprintf(stderr, "usage: razor import-rpms DIR\n");
@@ -654,15 +749,17 @@ command_import_rpms(int argc, const char *argv[])
        while (de = readdir(dir), de != NULL) {
                len = strlen(de->d_name);
                if (len < 5 || strcmp(de->d_name + len - 4, ".rpm") != 0)
-                   continue;
+                       continue;
                snprintf(filename, sizeof filename,
                         "%s/%s", dirname, de->d_name);
-               rpm = razor_rpm_open(filename);
-               if (rpm == NULL) {
-                       fprintf(stderr,
-                               "failed to open rpm \"%s\"\n", filename);
+               atomic = razor_atomic_open("Read RPM");
+               rpm = razor_rpm_open(filename, atomic);
+               if (rpm == NULL)
+                       fprintf(stderr, "%s\n",
+                               razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
+               if (rpm == NULL)
                        continue;
-               }
                if (razor_importer_add_rpm(importer, rpm)) {
                        fprintf(stderr, "couldn't import %s\n", filename);
                        break;
@@ -681,17 +778,22 @@ command_import_rpms(int argc, const char *argv[])
        printf("\nsaving\n");
        set = razor_importer_finish(importer);
 
-       razor_set_write(set, repo_filename, RAZOR_SECTION_ALL);
-       razor_set_destroy(set);
-       printf("wrote %s\n", repo_filename);
+       atomic = razor_atomic_open("Update system database");
+       razor_set_write(set, atomic, repo_filename, RAZOR_SECTION_ALL);
+       razor_set_unref(set);
+       retval = razor_atomic_commit(atomic);
+       if (retval)
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+       else
+               printf("wrote %s\n", repo_filename);
+       razor_atomic_destroy(atomic);
 
-       return 0;
+       return retval;
 }
 
-static const char *
+static char *
 rpm_filename(const char *name, const char *version, const char *arch)
 {
-       static char file[PATH_MAX];
        const char *v;
  
        /* Skip epoch */
@@ -701,9 +803,7 @@ rpm_filename(const char *name, const char *version, const char *arch)
        else
                v = version;
 
-       snprintf(file, sizeof file, "%s-%s.%s.rpm", name, v, arch);
-
-       return file;
+       return razor_concat(name, "-", v, ".", arch, ".rpm", NULL);
 }
 
 static int
@@ -713,7 +813,7 @@ download_packages(struct razor_set *system, struct razor_set *next)
        struct razor_package *package;
        enum razor_install_action action;
        const char *name, *version, *arch;
-       char file[PATH_MAX], url[256];
+       char *file, *url, *s;
        int errors = 0, count;
 
        ii = razor_set_create_install_iterator(system, next);
@@ -727,13 +827,14 @@ download_packages(struct razor_set *system, struct razor_set *next)
                                          RAZOR_DETAIL_ARCH, &arch,
                                          RAZOR_DETAIL_LAST);
                
-               snprintf(url, sizeof url,
-                        "%s/Packages/%s",
-                        yum_url, rpm_filename(name, version, arch));
-               snprintf(file, sizeof file,
-                        "rpms/%s", rpm_filename(name, version, arch));
+               s = rpm_filename(name, version, arch);
+               url = razor_concat(yum_url, "/Packages/", s, NULL);
+               file = razor_concat("rpms/", s, NULL);
+               free(s);
                if (download_if_missing(url, file) < 0)
                        errors++;
+               free(file);
+               free(url);
        }
        razor_install_iterator_destroy(ii);
 
@@ -746,7 +847,8 @@ download_packages(struct razor_set *system, struct razor_set *next)
 }
 
 static struct razor_set *
-relocate_packages(struct razor_set *set, struct razor_relocations *relocations)
+relocate_packages(struct razor_set *set, struct razor_atomic *atomic,
+                 struct razor_relocations *relocations)
 {
        int i;
        struct razor_importer *importer;
@@ -760,7 +862,7 @@ relocate_packages(struct razor_set *set, struct razor_relocations *relocations)
        const char *preunprog, *preun, *postunprog, *postun;
        const char *install_prefix;
        const char *const *prefixes;
-       char file[PATH_MAX];
+       char *file, *s;
        uint32_t flags;
 
        importer = razor_importer_create();
@@ -779,11 +881,12 @@ relocate_packages(struct razor_set *set, struct razor_relocations *relocations)
                                           RAZOR_DETAIL_POSTUNPROG, &postunprog,
                                           RAZOR_DETAIL_POSTUN, &postun,
                                           RAZOR_DETAIL_LAST)) {
-               snprintf(file, sizeof file,
-                        "rpms/%s", rpm_filename(name, version, arch));
-               rpm = razor_rpm_open(file);
+               s = rpm_filename(name, version, arch);
+               file = razor_concat("rpms/", s, NULL);
+               free(s);
+               rpm = razor_rpm_open(file, atomic);
+               free(file);
                if (rpm == NULL) {
-                       fprintf(stderr, "failed to open rpm %s\n", file);
                        razor_package_iterator_destroy(pkg_iter);
                        razor_importer_destroy(importer);
                        return NULL;
@@ -834,12 +937,13 @@ relocate_packages(struct razor_set *set, struct razor_relocations *relocations)
 
 static int
 install_package(struct razor_transaction *trans, struct razor_set *set,
-               struct razor_package *package,
-               struct razor_relocations *relocations)
+               struct razor_atomic *atomic, struct razor_package *package,
+               struct razor_relocations *relocations, int install_count,
+               enum razor_stage_type stage)
 {
        int retval;
        const char *name, *version, *arch;
-       char file[PATH_MAX];
+       char *file, *s;
        struct razor_rpm *rpm;
 
        razor_package_get_details(set, package,
@@ -848,45 +952,57 @@ install_package(struct razor_transaction *trans, struct razor_set *set,
                                  RAZOR_DETAIL_ARCH, &arch,
                                  RAZOR_DETAIL_LAST);
 
-       printf("install %s-%s\n", name, version);
+       if (stage & RAZOR_STAGE_SCRIPTS_PRE)
+               printf("install %s-%s\n", name, version);
 
-       snprintf(file, sizeof file,
-                "rpms/%s", rpm_filename(name, version, arch));
-       rpm = razor_rpm_open(file);
+       s = rpm_filename(name, version, arch);
+       file = razor_concat("rpms/", s, NULL);
+       free(s);
+       rpm = razor_rpm_open(file, atomic);
+       free(file);
        if (rpm == NULL) {
-               fprintf(stderr, "failed to open rpm %s\n", file);
+               fprintf(stderr, "%s\n",
+                       razor_atomic_get_error_msg(atomic));
                return -1;
        }
        if (relocations)
                razor_rpm_set_relocations(rpm, relocations);
        razor_transaction_fixup_package(trans, package, rpm);
-       retval = razor_rpm_install(rpm, install_root, 1);
-       if (retval < 0)
-               fprintf(stderr, "failed to install rpm %s\n", file);
+       retval = razor_rpm_install(rpm, atomic, install_root, install_count,
+                                  stage);
+       if (retval < 0) {
+               s = rpm_filename(name, version, arch);
+               fprintf(stderr, "%s: %s\n", s,
+                       razor_atomic_get_error_msg(atomic));
+               free(s);
+       }
        razor_rpm_close(rpm);
        return retval;
 }
 
 static int
-update_packages(struct razor_transaction *trans, struct razor_set *system,
-               struct razor_set *next, struct razor_relocations *relocations)
+update_packages(struct razor_transaction *trans,
+               struct razor_install_iterator *ii, struct razor_set *system,
+               struct razor_set *next, struct razor_atomic *atomic,
+               struct razor_relocations *relocations,
+               enum razor_stage_type stage)
 {
-       struct razor_install_iterator *ii;
        struct razor_package *package;
        enum razor_install_action action;
        int retval = 0, count;
 
-       ii = razor_set_create_install_iterator(system, next);
+       razor_install_iterator_rewind(ii);
+
        while (!retval && razor_install_iterator_next(ii, &package,
                                                      &action, &count)) {
                if (action == RAZOR_INSTALL_ACTION_ADD)
-                       retval = install_package(trans, next, package,
-                                                relocations);
+                       retval = install_package(trans, next, atomic, package,
+                                                relocations, count, stage);
                else if (action == RAZOR_INSTALL_ACTION_REMOVE)
-                       retval = razor_package_remove(system, next, package,
-                                                     install_root, count);
+                       retval = razor_package_remove(system, next, atomic,
+                                                     package, install_root,
+                                                     count, stage);
        }
-       razor_install_iterator_destroy(ii);
 
        return retval;
 }
@@ -898,12 +1014,22 @@ command_install_or_update(int argc, const char *argv[], int do_update)
        struct razor_relocations *relocations=NULL;
        struct razor_set *system, *upstream, *next, *set;
        struct razor_transaction *trans;
-       int i, len, dependencies = 1;
+       struct razor_atomic *atomic;
+       struct razor_install_iterator *ii;
+       int i, retval, len, dependencies = 1;
        char *oldpath;
 
-       root = razor_root_open(install_root);
-       if (root == NULL)
+       if (do_update)
+               atomic = razor_atomic_open("Update packages");
+       else
+               atomic = razor_atomic_open("Install packages");
+
+       root = razor_root_open(install_root, atomic);
+       if (root == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
+       }
 
        for (i = 0; i < argc; i++) {
                if (strcmp(argv[i], "--no-dependencies") == 0)
@@ -933,19 +1059,30 @@ command_install_or_update(int argc, const char *argv[], int do_update)
                        break;
        }
 
-       system = razor_root_get_system_set(root);
-       upstream = razor_set_open(rawhide_repo_filename);
+       upstream = razor_set_open(rawhide_repo_filename, atomic);
        if (upstream == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
                razor_root_close(root);
+               razor_atomic_destroy(atomic);
                return 1;
        }
 
        if (relocations) {
-               set = relocate_packages(upstream, relocations);
-               razor_set_destroy(upstream);
+               set = relocate_packages(upstream, atomic, relocations);
+               if (set == NULL) {
+                       fprintf(stderr, "%s\n",
+                               razor_atomic_get_error_msg(atomic));
+                       razor_root_close(root);
+                       razor_atomic_destroy(atomic);
+                       razor_set_unref(upstream);
+                       return 1;
+               }
+               razor_set_unref(upstream);
                upstream = set;
        }
 
+       system = razor_set_ref(razor_root_get_system_set(root));
+
        trans = razor_transaction_create(system, upstream);
 
        if (i == argc && do_update)
@@ -957,8 +1094,10 @@ command_install_or_update(int argc, const char *argv[], int do_update)
                if (mark_packages_for_update(trans, upstream, argv[i]) == 0) {
                        fprintf(stderr, "no package matched %s\n", argv[i]);
                        razor_transaction_destroy(trans);
-                       razor_set_destroy(upstream);
+                       razor_set_unref(upstream);
+                       razor_set_unref(system);
                        razor_root_close(root);
+                       razor_atomic_destroy(atomic);
                        return 1;
                }
        }
@@ -967,41 +1106,67 @@ command_install_or_update(int argc, const char *argv[], int do_update)
                razor_transaction_resolve(trans);
                if (razor_transaction_describe(trans) > 0) {
                        razor_transaction_destroy(trans);
-                       razor_set_destroy(upstream);
+                       razor_set_unref(upstream);
+                       razor_set_unref(system);
                        razor_root_close(root);
+                       razor_atomic_destroy(atomic);
                        return 1;
                }
        }
 
-       if (mkdir("rpms", 0777) && errno != EEXIST) {
-               fprintf(stderr, "failed to create rpms directory.\n");
+       if (razor_atomic_create_dir(atomic, "rpms",
+                                   S_IRWXU | S_IRWXG | S_IRWXO)) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
                razor_transaction_destroy(trans);
-               razor_set_destroy(upstream);
+               razor_set_unref(upstream);
+               razor_set_unref(system);
                razor_root_close(root);
+               razor_atomic_destroy(atomic);
                return 1;
        }
 
        next = razor_transaction_commit(trans);
 
        if (download_packages(system, next) < 0) {
-               razor_set_destroy(next);
+               razor_set_unref(next);
                razor_transaction_destroy(trans);
-               razor_set_destroy(upstream);
+               razor_set_unref(upstream);
+               razor_set_unref(system);
                razor_root_close(root);
+               razor_atomic_destroy(atomic);
                 return 1;
         }
 
-       update_packages(trans, system, next, relocations);
+       ii = razor_set_create_install_iterator(system, next);
+
+       update_packages(trans, ii, system, next, atomic, relocations,
+                       RAZOR_STAGE_SCRIPTS_PRE);
+       update_packages(trans, ii, system, next, atomic, relocations,
+                       RAZOR_STAGE_FILES);
 
        razor_root_update(root, next);
 
+       razor_set_unref(upstream);
+
+       (void)razor_root_commit(root);
+       retval = razor_atomic_commit(atomic);
+       if (retval)
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+       else
+               update_packages(trans, ii, system, next, atomic, relocations,
+                               RAZOR_STAGE_SCRIPTS_POST);
+
        razor_transaction_destroy(trans);
        if (relocations)
                razor_relocations_destroy(relocations);
-       razor_set_destroy(next);
-       razor_set_destroy(upstream);
+       razor_install_iterator_destroy(ii);
+
+       razor_atomic_destroy(atomic);
 
-       return razor_root_commit(root);
+       razor_set_unref(next);
+       razor_set_unref(system);
+
+       return retval;
 }
 
 static int
@@ -1025,6 +1190,7 @@ command_init(int argc, const char *argv[])
 static int
 command_download(int argc, const char *argv[])
 {
+       struct razor_atomic *atomic;
        struct razor_set *set;
        struct razor_package_iterator *pi;
        struct razor_package *package;
@@ -1032,12 +1198,24 @@ command_download(int argc, const char *argv[])
        char url[256], file[256];
        int matches = 0;
 
-       if (mkdir("rpms", 0777) && errno != EEXIST) {
-               fprintf(stderr, "failed to create rpms directory.\n");
+       atomic = razor_atomic_open("Download packages");
+
+       if (razor_atomic_create_dir(atomic, "rpms", 
+                                   S_IRWXU | S_IRWXG | S_IRWXO)) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
+               return 1;
+       }
+
+       set = razor_set_open(rawhide_repo_filename, atomic);
+
+       if (razor_atomic_commit(atomic)) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
        }
+       razor_atomic_destroy(atomic);
 
-       set = razor_set_open(rawhide_repo_filename);
        pi = razor_package_iterator_create(set);
        while (razor_package_iterator_next(pi, &package,
                                           RAZOR_DETAIL_NAME, &name,
@@ -1056,7 +1234,7 @@ command_download(int argc, const char *argv[])
                download_if_missing(url, file);
        }
        razor_package_iterator_destroy(pi);
-       razor_set_destroy(set);
+       razor_set_unref(set);
 
        if (matches == 0)
                fprintf(stderr, "no packages matched \"%s\"\n", pattern);
@@ -1071,15 +1249,20 @@ command_download(int argc, const char *argv[])
 static int
 command_info(int argc, const char *argv[])
 {
+       struct razor_atomic *atomic;
        struct razor_set *set;
        struct razor_package_iterator *pi;
        struct razor_package *package;
        const char *pattern = argv[0], *name, *version, *arch;
        const char *summary, *description, *url, *license;
 
-       set = razor_root_open_read_only(install_root);
-       if (set == NULL)
+       atomic = razor_atomic_open("Package info");
+       set = razor_root_open_read_only(install_root, atomic);
+       if (set == NULL) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
+       }
 
        pi = razor_package_iterator_create(set);
        while (razor_package_iterator_next(pi, &package,
@@ -1108,7 +1291,8 @@ command_info(int argc, const char *argv[])
                printf ("\n");
        }
        razor_package_iterator_destroy(pi);
-       razor_set_destroy(set);
+       razor_set_unref(set);
+       razor_atomic_destroy(atomic);
 
        return 0;
 }
@@ -1118,6 +1302,7 @@ command_info(int argc, const char *argv[])
 static int
 command_search(int argc, const char *argv[])
 {
+       struct razor_atomic *atomic;
        struct razor_set *set;
        struct razor_package_iterator *pi;
        struct razor_package *package;
@@ -1132,9 +1317,14 @@ command_search(int argc, const char *argv[])
 
        snprintf(pattern, sizeof pattern, "*%s*", argv[0]);
 
-       set = razor_set_open(rawhide_repo_filename);
-       if (set == NULL)
+       atomic = razor_atomic_open("Search packages");
+       set = razor_set_open(rawhide_repo_filename, atomic);
+       if (set == NULL || razor_atomic_commit(atomic)) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
                return 1;
+       }
+       razor_atomic_destroy(atomic);
 
        pi = razor_package_iterator_create(set);
        while (razor_package_iterator_next(pi, &package,
@@ -1153,7 +1343,7 @@ command_search(int argc, const char *argv[])
                        printf("%s-%s.%s: %s\n", name, version, arch, summary);
        }
        razor_package_iterator_destroy(pi);
-       razor_set_destroy(set);
+       razor_set_unref(set);
 
        return 0;
 }
index 63b6ee0..623c41a 100644 (file)
--- a/src/rpm.c
+++ b/src/rpm.c
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
  * Copyright (C) 2008  Red Hat, Inc
- * Copyright (C) 2009  J. Ali Harlow <ali@juiblex.co.uk>
+ * Copyright (C) 2009, 2011  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
@@ -441,13 +441,16 @@ static struct razor_set *
 create_set_from_command_line(int argc, const char *argv[])
 {
        struct razor_importer *importer;
+       struct razor_atomic *atomic;
        struct razor_rpm *rpm;
        int i;
 
        importer = razor_importer_create();
 
        for (i = 0; i < argc; i++) {
-               rpm = razor_rpm_open(argv[i]);
+               atomic = razor_atomic_open("Read RPM");
+               rpm = razor_rpm_open(argv[i], atomic);
+               razor_atomic_destroy(atomic);
                if (rpm == NULL)
                        continue;
                if (razor_importer_add_rpm(importer, rpm))
@@ -462,17 +465,25 @@ create_set_from_command_line(int argc, const char *argv[])
 static void
 command_query(int argc, const char *argv[])
 {
+       struct razor_atomic *atomic;
        struct razor_set *set;
        struct razor_package_iterator *pi;
        struct razor_package *package;
        const char *name, *version, *arch;
 
+       atomic = razor_atomic_open("Query packages");
        if (option_package) {
                set = create_set_from_command_line(argc, argv);
                argc = 0;
                option_all = 1;
        } else {
-               set = razor_root_open_read_only(option_root);
+               set = razor_root_open_read_only(option_root, atomic);
+               if (!set) {
+                       fprintf(stderr, "%s\n",
+                               razor_atomic_get_error_msg(atomic));
+                       razor_atomic_destroy(atomic);
+                       return;
+               }
        }
 
        pi = get_query_packages(set, argc, argv);
@@ -509,25 +520,32 @@ command_query(int argc, const char *argv[])
 
        razor_package_iterator_destroy(pi);
 
-       razor_set_destroy(set);
-
-       return;
+       razor_set_unref(set);
+       razor_atomic_destroy(atomic);
 }
 
 static void
 command_verify(int argc, const char *argv[])
 {
+       struct razor_atomic *atomic;
        struct razor_set *set;
        struct razor_package_iterator *pi;
        struct razor_package *package;
        const char *name, *version, *arch;
 
+       atomic = razor_atomic_open("Verify packages");
        if (option_package) {
                set = create_set_from_command_line(argc, argv);
                argc = 0;
                option_all = 1;
        } else {
-               set = razor_root_open_read_only(option_root);
+               set = razor_root_open_read_only(option_root, atomic);
+               if (!set) {
+                       fprintf(stderr, "%s\n",
+                               razor_atomic_get_error_msg(atomic));
+                       razor_atomic_destroy(atomic);
+                       return;
+               }
        }
 
        pi = get_query_packages(set, argc, argv);
@@ -542,6 +560,7 @@ command_verify(int argc, const char *argv[])
        }
 
        razor_package_iterator_destroy(pi);
+       razor_atomic_destroy(atomic);
 }
 
 static void
@@ -561,6 +580,7 @@ update_package(enum razor_diff_action action,
 static void
 command_erase(int argc, const char *argv[])
 {
+       struct razor_atomic *atomic;
        struct razor_set *set, *upstream, *next;
        struct razor_transaction *trans;
        struct razor_package_query *query;
@@ -572,7 +592,15 @@ command_erase(int argc, const char *argv[])
                exit(1);
        }
 
-       set = razor_set_open(repo_filename);
+       atomic = razor_atomic_open("Erase packages");
+
+       set = razor_set_open(repo_filename, atomic);
+       if (!set || razor_atomic_commit(atomic)) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
+               exit(1);
+       }
+       razor_atomic_destroy(atomic);
        upstream = razor_set_create();
 
        trans = razor_transaction_create(set, upstream);
@@ -602,15 +630,16 @@ command_erase(int argc, const char *argv[])
                razor_set_diff(set, next, update_package, NULL);
 
        razor_transaction_destroy(trans);
-       razor_set_destroy(set);
-       razor_set_destroy(upstream);
+       razor_set_unref(set);
+       razor_set_unref(upstream);
 
-       razor_set_destroy(next);
+       razor_set_unref(next);
 }
 
 static void
 command_install(int argc, const char *argv[])
 {
+       struct razor_atomic *atomic;
        struct razor_set *set, *upstream, *next;
        struct razor_transaction *trans;
        struct razor_package_iterator *pi;
@@ -621,7 +650,14 @@ command_install(int argc, const char *argv[])
                exit(1);
        }
 
-       set = razor_set_open(repo_filename);
+       atomic = razor_atomic_open("Install packages");
+
+       set = razor_set_open(repo_filename, atomic);
+       if (!set || razor_atomic_commit(atomic)) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
+       }
+       razor_atomic_destroy(atomic);
        upstream = create_set_from_command_line(argc, argv);
 
        trans = razor_transaction_create(set, upstream);
@@ -648,15 +684,16 @@ command_install(int argc, const char *argv[])
                razor_set_diff(set, next, update_package, NULL);
 
        razor_transaction_destroy(trans);
-       razor_set_destroy(set);
-       razor_set_destroy(upstream);
+       razor_set_unref(set);
+       razor_set_unref(upstream);
 
-       razor_set_destroy(next);
+       razor_set_unref(next);
 }
 
 static void
 command_update(int argc, const char *argv[])
 {
+       struct razor_atomic *atomic;
        struct razor_set *set, *upstream, *next;
        struct razor_transaction *trans;
        struct razor_package_iterator *pi;
@@ -667,7 +704,14 @@ command_update(int argc, const char *argv[])
                exit(1);
        }
 
-       set = razor_set_open(repo_filename);
+       atomic = razor_atomic_open("Update packages");
+
+       set = razor_set_open(repo_filename, atomic);
+       if (!set || razor_atomic_commit(atomic)) {
+               fprintf(stderr, "%s\n", razor_atomic_get_error_msg(atomic));
+               razor_atomic_destroy(atomic);
+       }
+       razor_atomic_destroy(atomic);
        upstream = create_set_from_command_line(argc, argv);
 
        trans = razor_transaction_create(set, upstream);
@@ -694,10 +738,10 @@ command_update(int argc, const char *argv[])
                razor_set_diff(set, next, update_package, NULL);
 
        razor_transaction_destroy(trans);
-       razor_set_destroy(set);
-       razor_set_destroy(upstream);
+       razor_set_unref(set);
+       razor_set_unref(upstream);
 
-       razor_set_destroy(next);
+       razor_set_unref(next);
 }
 
 static int