ali@416: /* ali@416: * Copyright (C) 2012 J. Ali Harlow ali@416: * ali@416: * This program is free software; you can redistribute it and/or modify ali@416: * it under the terms of the GNU General Public License as published by ali@416: * the Free Software Foundation; either version 2 of the License, or ali@416: * (at your option) any later version. ali@416: * ali@416: * This program is distributed in the hope that it will be useful, ali@416: * but WITHOUT ANY WARRANTY; without even the implied warranty of ali@416: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ali@416: * GNU General Public License for more details. ali@416: * ali@416: * You should have received a copy of the GNU General Public License along ali@416: * with this program; if not, write to the Free Software Foundation, Inc., ali@416: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ali@416: */ ali@416: ali@416: #include "config.h" ali@416: ali@416: #if ENABLE_ATOMIC && !HAVE_WINDOWS_KTM ali@416: ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include "razor-internal.h" ali@416: ali@416: /* ali@416: * Emulated atomic support ali@416: * ali@416: * This implementation is better than nothing, but is certainly not atomic. ali@416: * It does have a couple of advantages over atomic-none: ali@416: * - If a file operation fails while a package is being installed we ali@416: * have a good chance of being able to rollback the transaction to ali@416: * a well-known state. ali@416: * - We behave similarly to atomic-ktm in that changes are not visible ali@416: * on disk to non-atomic operations (eg., scripts) until the atomic ali@416: * is committed. This makes the testsuite more likely to pick up ali@416: * problems that would otherwise only be found when using razor on ali@416: * an MS-Windows system which supports KTM. ali@416: */ ali@416: ali@416: #ifndef O_BINARY ali@416: #define O_BINARY 0 ali@416: #endif ali@416: ali@416: static void recursive_remove(const char *directory) ali@416: { ali@416: DIR *dp; ali@416: struct dirent *dirp; ali@416: char *buf; ali@416: ali@416: dp = opendir(directory); ali@416: while((dirp = readdir(dp))) { ali@416: if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) { ali@416: buf = malloc(strlen(directory) + strlen(dirp->d_name) ali@416: + 2); ali@416: sprintf(buf, "%s/%s", directory, dirp->d_name); ali@416: if (remove(buf) < 0) ali@416: recursive_remove(buf); ali@416: free(buf); ali@416: } ali@416: } ali@416: ali@416: rmdir(directory); ali@416: } ali@416: ali@416: RAZOR_EXPORT struct razor_atomic *razor_atomic_open(const char *description) ali@416: { ali@416: struct razor_atomic *atomic; ali@416: ali@416: atomic = zalloc(sizeof *atomic); ali@416: ali@416: atomic->toplevel = strdup(".atomic-XXXXXX"); ali@416: if (!mkdtemp(atomic->toplevel)) { ali@416: free(atomic->toplevel); ali@416: free(atomic); ali@416: return NULL; ali@416: } ali@416: ali@416: atomic->description = strdup(description); ali@416: ali@416: return atomic; ali@416: } ali@416: ali@416: RAZOR_EXPORT int razor_atomic_commit(struct razor_atomic *atomic) ali@416: { ali@416: struct atomic_action *actions; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: if (atomic->actions) { ali@416: actions = atomic_action_list_reverse(atomic->actions); ali@416: atomic->actions = NULL; ali@416: actions = atomic_action_do(atomic, actions); ali@416: atomic_action_free(actions); ali@416: } ali@416: ali@416: if (atomic->toplevel) { ali@416: recursive_remove(atomic->toplevel); ali@416: free(atomic->toplevel); ali@416: atomic->toplevel = NULL; ali@416: } ali@416: ali@416: return !!atomic->error_str; ali@416: } ali@416: ali@416: RAZOR_EXPORT void razor_atomic_destroy(struct razor_atomic *atomic) ali@416: { ali@416: if (atomic->toplevel) { ali@416: recursive_remove(atomic->toplevel); ali@416: free(atomic->toplevel); ali@416: atomic->toplevel = NULL; ali@416: } ali@416: ali@416: free(atomic->error_path); ali@416: free(atomic->error_str); ali@416: free(atomic->error_msg); ali@416: free(atomic); ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root, ali@416: const char *path) ali@416: { ali@416: struct atomic_action *a; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: a = atomic_action_new(ACTION_MAKE_DIRS); ali@416: a->args.path = strdup(path); ali@416: a->args.u.make_dirs.root = strdup(root); ali@416: atomic->actions = atomic_action_list_prepend(atomic->actions, a); ali@416: ali@416: return 0; ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_remove(struct razor_atomic *atomic, const char *path) ali@416: { ali@416: struct atomic_action *a; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: a = atomic_action_new(ACTION_REMOVE); ali@416: a->args.path = strdup(path); ali@416: atomic->actions = atomic_action_list_prepend(atomic->actions, a); ali@416: ali@416: return 0; ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath, ali@416: const char *newpath) ali@416: { ali@416: struct atomic_action *a; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: a = atomic_action_new(ACTION_MOVE); ali@416: a->args.path = strdup(oldpath); ali@416: a->args.u.move.dest = strdup(newpath); ali@416: atomic->actions = atomic_action_list_prepend(atomic->actions, a); ali@416: ali@416: return 0; ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname, ali@416: mode_t mode) ali@416: { ali@416: struct atomic_action *a; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: a = atomic_action_new(ACTION_CREATE_DIR); ali@416: a->args.path = strdup(dirname); ali@416: a->args.u.create_dir.mode = mode; ali@416: atomic->actions = atomic_action_list_prepend(atomic->actions, a); ali@416: ali@416: return 0; ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target, ali@416: const char *path) ali@416: { ali@416: #if HAVE_SYMLINK ali@416: struct atomic_action *a; ali@416: #endif ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: #if HAVE_SYMLINK ali@416: a = atomic_action_new(ACTION_CREATE_SYMLINK); ali@416: a->args.path = strdup(path); ali@416: a->args.u.create_symlink.target = strdup(target); ali@416: atomic->actions = atomic_action_list_prepend(atomic->actions, a); ali@416: ali@416: return 0; ali@416: #else ali@416: razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported " ali@416: "on this platform"); ali@416: ali@416: return -1; ali@416: #endif ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@416: razor_atomic_create_file(struct razor_atomic *atomic, const char *filename, ali@416: mode_t mode) ali@416: { ali@416: int fd; ali@416: struct atomic_action *a; ali@416: char *tmpnam; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: atomic->error_path = strdup(filename); ali@416: tmpnam = atomic_action_attic_tmpnam(atomic); ali@416: fd = open(tmpnam, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, ali@416: mode & (S_IRWXU | S_IRWXG | S_IRWXO)); ali@416: ali@416: if (fd == -1) ali@416: razor_atomic_set_error_str(atomic, NULL, strerror(errno)); ali@416: else { ali@416: a = atomic_action_new(ACTION_MOVE); ali@416: a->args.path = tmpnam; ali@416: a->args.u.move.dest = strdup(filename); ali@416: atomic->actions = atomic_action_list_prepend(atomic->actions, ali@416: a); ali@416: } ali@416: ali@416: return fd; ali@416: } ali@416: ali@416: #endif /* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */