From 18b20a897965e55f848f05125507493cbb381149 Mon Sep 17 00:00:00 2001 From: J. Ali Harlow Date: Thu, 10 Nov 2011 10:35:21 +0000 Subject: [PATCH] Use Windows KTM (atomic transactions) where supported. Increment current header version to 2 --- configure.ac | 25 +- librazor/Makefile.am | 1 + librazor/atomic.c | 1002 +++++++++++++++++++++++++++++++++++++++++++++ librazor/merger.c | 35 ++- librazor/razor-internal.h | 8 +- librazor/razor.c | 253 ++++++++---- librazor/razor.h | 150 ++++++- librazor/root.c | 161 ++++---- librazor/rpm.c | 233 ++++++----- librazor/transaction.c | 17 +- librazor/util.c | 196 +++------ src/main.c | 440 ++++++++++++++------ src/rpm.c | 82 +++- 13 files changed, 2040 insertions(+), 563 deletions(-) create mode 100644 librazor/atomic.c diff --git a/configure.ac b/configure.ac index 6331fae..bb36d89 100644 --- a/configure.ac +++ b/configure.ac @@ -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 +#include +], [(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) diff --git a/librazor/Makefile.am b/librazor/Makefile.am index e887ad3..bc5c469 100644 --- a/librazor/Makefile.am +++ b/librazor/Makefile.am @@ -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 index 0000000..ffc5bbf --- /dev/null +++ b/librazor/atomic.c @@ -0,0 +1,1002 @@ +/* + * Copyright (C) 2011 J. Ali Harlow + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#ifdef MSWIN_API +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_WINDOWS_KTM +#include +#include +#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; +} diff --git a/librazor/merger.c b/librazor/merger.c index ebf1c86..4fcd36d 100644 --- a/librazor/merger.c +++ b/librazor/merger.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009, 2010 J. Ali Harlow + * Copyright (C) 2009-2011 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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; diff --git a/librazor/razor-internal.h b/librazor/razor-internal.h index bc2b7bf..22f193b 100644 --- a/librazor/razor-internal.h +++ b/librazor/razor-internal.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009 J. Ali Harlow + * Copyright (C) 2009, 2011 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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 { diff --git a/librazor/razor.c b/librazor/razor.c index 5a6264e..228fbf9 100644 --- a/librazor/razor.c +++ b/librazor/razor.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009, 2010 J. Ali Harlow + * Copyright (C) 2009-2011 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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_versionRAZOR_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); } diff --git a/librazor/razor.h b/librazor/razor.h index 49a24b0..0806091 100644 --- a/librazor/razor.h +++ b/librazor/razor.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009 J. Ali Harlow + * Copyright (C) 2009, 2011 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,21 @@ #define _RAZOR_H_ #include +#include + +/* 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_ */ diff --git a/librazor/root.c b/librazor/root.c index 3ca58cf..edac683 100644 --- a/librazor/root.c +++ b/librazor/root.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009 J. Ali Harlow + * Copyright (C) 2009, 2011 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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; diff --git a/librazor/rpm.c b/librazor/rpm.c index cfcb3e8..db37119 100644 --- a/librazor/rpm.c +++ b/librazor/rpm.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009 J. Ali Harlow + * Copyright (C) 2009, 2011 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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; } diff --git a/librazor/transaction.c b/librazor/transaction.c index 23c4a2e..13d6a9a 100644 --- a/librazor/transaction.c +++ b/librazor/transaction.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009 J. Ali Harlow + * Copyright (C) 2009, 2011 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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 } } diff --git a/librazor/util.c b/librazor/util.c index 888e22c..584c260 100644 --- a/librazor/util.c +++ b/librazor/util.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009 J. Ali Harlow + * Copyright (C) 2009, 2011 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,6 +33,8 @@ #ifdef MSWIN_API #include #include +#else +#include #endif #if HAVE_SYS_MMAN_H #include @@ -46,137 +48,9 @@ #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 +} diff --git a/src/main.c b/src/main.c index 8d5c097..42330de 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009 J. Ali Harlow + * Copyright (C) 2009, 2011 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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; } diff --git a/src/rpm.c b/src/rpm.c index 63b6ee0..623c41a 100644 --- a/src/rpm.c +++ b/src/rpm.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009 J. Ali Harlow + * Copyright (C) 2009, 2011 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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 -- 1.7.1