ali@416: /* ali@475: * Copyright (C) 2012, 2014, 2016 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@423: #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@475: void *dp; ali@475: char *name, *buf; ali@416: ali@475: dp = razor_uri_opendir(directory, NULL); ali@475: ali@475: if (dp) { ali@475: while((name = razor_uri_readdir(dp, NULL))) { ali@475: buf = razor_concat(directory, "/", name, NULL); ali@475: free(name); ali@475: if (razor_uri_unlink(buf, NULL) < 0) ali@416: recursive_remove(buf); ali@416: free(buf); ali@416: } ali@475: ali@475: razor_uri_closedir(dp, NULL); 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->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@423: return razor_atomic_in_error_state(atomic); ali@416: } ali@416: ali@416: RAZOR_EXPORT void razor_atomic_destroy(struct razor_atomic *atomic) ali@416: { ali@458: if (atomic->actions) { ali@458: atomic_action_free(atomic->actions); ali@458: atomic->actions = NULL; ali@458: } ali@458: 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@423: if (atomic->error) ali@423: razor_error_free(atomic->error); ali@423: ali@458: free(atomic->description); ali@458: ali@416: free(atomic); ali@416: } ali@416: ali@435: /* ali@435: * We need a toplevel directory in which to hold temporary files ali@435: * before they are committed. Since we can generally assume that ali@444: * we have write permissions anywhere on the filesystem in ali@444: * question, the best location is at the relevant mount point. ali@444: * The most common case where this assumption fails is when ali@444: * testing, when the current directory is a good choice. ali@435: */ ali@435: ali@435: static int ali@475: razor_atomic_set_toplevel_from_uri(struct razor_atomic *atomic, ali@475: const char *uri) ali@435: { ali@435: if (razor_atomic_in_error_state(atomic)) ali@435: return -1; ali@435: ali@435: if (atomic->toplevel) ali@435: return 0; ali@435: ali@435: #ifdef MSWIN_API ali@475: atomic->toplevel = razor_uri_mkdtemp_near(uri, "atomic-XXXXXX", ali@475: &atomic->error); ali@435: #else ali@475: atomic->toplevel = razor_uri_mkdtemp_near(uri, ".atomic-XXXXXX", ali@475: &atomic->error); ali@435: #endif ali@435: ali@435: return !atomic->toplevel; ali@435: } ali@435: ali@475: /* ali@475: * If root_uri is empty, then uri can be a URI (but not a relative-ref; ie., ali@475: * a scheme must be specified). If root_uri is non-empty, then it must be a ali@475: * URI (but not a relative-ref) and uri must be a non-empty path. ali@475: */ ali@416: RAZOR_EXPORT int ali@475: razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root_uri, ali@475: const char *uri) ali@416: { ali@416: struct atomic_action *a; ali@475: struct razor_uri ru; ali@475: struct razor_error *tmp_error = NULL; ali@475: char *fakeroot = NULL; ali@416: ali@475: razor_atomic_set_toplevel_from_uri(atomic, *root_uri ? root_uri : uri); ali@435: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@475: if (!*root_uri) { ali@475: if (razor_uri_parse(&ru, uri, &tmp_error)) { ali@475: razor_atomic_propagate_error(atomic, tmp_error, NULL); ali@475: return -1; ali@475: } ali@475: if (*ru.path == '/') { ali@475: free(ru.path); ali@475: ru.path = strdup("/"); ali@475: } else { ali@475: free(ru.path); ali@475: ru.path = strdup(""); ali@475: } ali@475: fakeroot = razor_uri_recompose(&ru); ali@475: razor_uri_destroy(&ru); ali@475: root_uri = fakeroot; ali@475: uri += strlen(fakeroot); ali@475: } ali@475: ali@416: a = atomic_action_new(ACTION_MAKE_DIRS); ali@475: a->args.uri = strdup(uri); ali@475: a->args.u.make_dirs.root = strdup(root_uri); ali@475: atomic->actions = atomic_action_list_prepend(atomic->actions, a); ali@475: ali@475: if (fakeroot) ali@475: free(fakeroot); ali@475: ali@475: return 0; ali@475: } ali@475: ali@475: RAZOR_EXPORT int ali@475: razor_atomic_remove(struct razor_atomic *atomic, const char *uri) ali@475: { ali@475: struct atomic_action *a; ali@475: ali@475: razor_atomic_set_toplevel_from_uri(atomic, uri); ali@475: ali@475: if (razor_atomic_in_error_state(atomic)) ali@475: return -1; ali@475: ali@475: a = atomic_action_new(ACTION_REMOVE); ali@475: a->args.uri = strdup(uri); 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@475: razor_atomic_rename_file(struct razor_atomic *atomic, const char *old_uri, ali@475: const char *new_uri) ali@416: { ali@416: struct atomic_action *a; ali@416: ali@475: razor_atomic_set_toplevel_from_uri(atomic, new_uri); ali@435: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@475: a = atomic_action_new(ACTION_MOVE); ali@475: a->args.uri = strdup(old_uri); ali@475: a->args.u.move.dest = strdup(new_uri); 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@475: razor_atomic_create_dir(struct razor_atomic *atomic, const char *uri, ali@416: mode_t mode) ali@416: { ali@416: struct atomic_action *a; ali@416: ali@475: razor_atomic_set_toplevel_from_uri(atomic, uri); ali@435: 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@475: a->args.uri = strdup(uri); 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@475: const char *uri) ali@416: { ali@416: #if HAVE_SYMLINK ali@416: struct atomic_action *a; ali@435: ali@475: razor_atomic_set_toplevel_from_uri(atomic, uri); 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@475: a->args.uri = strdup(uri); 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@447: atomic->error = razor_error_new_str(RAZOR_POSIX_ERROR, ENOSYS, NULL, ali@423: "Symbolic links not supported " ali@423: "on this platform"); ali@416: ali@416: return -1; ali@416: #endif ali@416: } ali@416: ali@416: RAZOR_EXPORT int ali@475: razor_atomic_create_file(struct razor_atomic *atomic, const char *uri, ali@416: mode_t mode) ali@416: { ali@416: int fd; ali@416: struct atomic_action *a; ali@416: char *tmpnam; ali@416: ali@475: razor_atomic_set_toplevel_from_uri(atomic, uri); ali@435: ali@416: if (razor_atomic_in_error_state(atomic)) ali@416: return -1; ali@416: ali@416: tmpnam = atomic_action_attic_tmpnam(atomic); ali@475: fd = razor_uri_open(tmpnam, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, ali@475: mode & (S_IRWXU | S_IRWXG | S_IRWXO), ali@475: &atomic->error); ali@416: ali@475: if (fd >= 0) { ali@416: a = atomic_action_new(ACTION_MOVE); ali@475: a->args.uri = tmpnam; ali@475: a->args.u.move.dest = strdup(uri); 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 */