ali@416: /* ali@475: * Copyright (C) 2012, 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@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include ali@416: #include "razor-internal.h" ali@416: ali@416: char *atomic_action_attic_tmpnam(struct razor_atomic *atomic) ali@416: { ali@416: char filename[17]; ali@416: sprintf(filename,"%03X",atomic->next_file_tag++); ali@416: return razor_concat(atomic->toplevel, "/", filename, NULL); ali@416: } ali@416: ali@416: static struct atomic_action * ali@416: atomic_action_list_pop_head(struct atomic_action **list) ali@416: { ali@416: struct atomic_action *head; ali@416: ali@416: head = *list; ali@416: if (head) { ali@416: *list = head->next; ali@416: head->next = NULL; ali@416: } ali@416: ali@416: return head; ali@416: } ali@416: ali@416: struct atomic_action * ali@416: atomic_action_list_prepend(struct atomic_action *list, ali@416: struct atomic_action *action) ali@416: { ali@416: assert(action->next == NULL); ali@416: ali@416: action->next=list; ali@416: ali@416: return action; ali@416: } ali@416: ali@416: static struct atomic_action * ali@416: atomic_action_list_concat(struct atomic_action *list1, ali@416: struct atomic_action *list2) ali@416: { ali@416: struct atomic_action *action; ali@416: ali@416: if (!list1) ali@416: return list2; ali@416: ali@416: for(action=list1;action->next;action=action->next) ali@416: ; ali@416: ali@416: action->next=list2; ali@416: ali@416: return list1; ali@416: } ali@416: ali@416: void atomic_action_free(struct atomic_action *action) ali@416: { ali@416: struct atomic_action *a; ali@416: ali@416: while(action) { ali@416: a = atomic_action_list_pop_head(&action); ali@416: ali@475: free(a->args.uri); ali@416: ali@416: switch(a->type) { ali@416: case ACTION_MAKE_DIRS: ali@416: free(a->args.u.make_dirs.root); ali@416: break; ali@416: case ACTION_MOVE: ali@416: free(a->args.u.move.dest); ali@416: break; ali@416: #if HAVE_SYMLINK ali@416: case ACTION_CREATE_SYMLINK: ali@416: free(a->args.u.create_symlink.target); ali@416: break; ali@416: #endif ali@416: case ACTION_REMOVE: ali@416: case ACTION_CREATE_DIR: ali@416: break; ali@416: } ali@416: ali@416: free(a); ali@416: } ali@416: } ali@416: ali@416: struct atomic_action *atomic_action_new(enum atomic_action_type type) ali@416: { ali@416: struct atomic_action *action; ali@416: ali@416: action = zalloc(sizeof *action); ali@416: action->type = type; ali@416: ali@416: return action; ali@416: } ali@416: ali@416: struct atomic_action *atomic_action_list_reverse(struct atomic_action *list) ali@416: { ali@416: struct atomic_action *prev = NULL, *next; ali@416: ali@416: while(list) { ali@416: next = list->next; ali@416: list->next = prev; ali@416: prev = list; ali@416: list = next; ali@416: } ali@416: ali@416: return prev; ali@416: } ali@416: ali@416: /* ali@416: * All action_ functions take 1 action and return a list of primitive ali@416: * actions they took (such that the first action in the list is the ali@416: * last action they took). Primitive actions are always reversable. ali@416: * ali@416: * On failure, an error should be set on the atomic object (if it is ali@416: * not already set), the action freed and NULL returned. ali@416: * ali@416: * Whether they succeed or fail, they take ownership of the passed ali@416: * action and should thus either free it or return it back to their ali@416: * caller as appropriate. ali@416: * ali@416: * A NULL return means that nothing was done which may, or may not, ali@416: * indicate an error. ali@416: */ ali@416: ali@416: static struct atomic_action * ali@416: atomic_action_make_dirs(struct razor_atomic *atomic, ali@416: struct atomic_action *action) ali@416: { ali@416: char buffer[PATH_MAX], *p; ali@416: const char *slash, *next; ali@416: struct atomic_action *primitives = NULL; ali@416: struct atomic_action *prim; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) { ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } ali@416: ali@416: strcpy(buffer, action->args.u.make_dirs.root); ali@416: p = buffer + strlen(buffer); ali@475: slash = action->args.uri; ali@475: if (p > buffer && p[-1] != ':' && p[-1] != '/' && *slash != '/') ali@475: *p++ = '/'; ali@416: for (; *slash != '\0'; slash = next) { ali@416: next = strchr(slash + 1, '/'); ali@416: if (next == NULL) ali@416: break; ali@416: ali@416: memcpy(p, slash, next - slash); ali@416: p += next - slash; ali@416: *p = '\0'; ali@416: ali@416: if (razor_valid_root_name(buffer)) ali@416: continue; ali@416: ali@416: prim = atomic_action_new(ACTION_CREATE_DIR); ali@475: prim->args.uri = strdup(buffer); ali@416: prim->args.u.create_dir.mode = S_IRWXU | S_IRWXG | S_IRWXO; ali@416: primitives = atomic_action_list_prepend(primitives, prim); ali@416: } ali@416: primitives = atomic_action_list_reverse(primitives); ali@416: ali@458: atomic_action_free(action); ali@458: ali@416: return atomic_action_do(atomic, primitives); ali@416: } ali@416: ali@416: static struct atomic_action * ali@416: atomic_action_remove(struct razor_atomic *atomic, struct atomic_action *action) ali@416: { ali@475: void *dir; ali@475: char *name; ali@416: struct atomic_action *prim; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) { ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } ali@416: ali@416: /* ali@416: * Non-empty directories should NOT be removed ali@416: */ ali@475: dir = razor_uri_opendir(action->args.uri, NULL); ali@475: if (dir) { ali@475: name = razor_uri_readdir(dir, NULL); ali@475: razor_uri_closedir(dir, NULL); ali@475: if (name) { ali@475: free(name); ali@475: atomic_action_free(action); ali@475: action = NULL; ali@475: } ali@416: } ali@416: ali@416: if (action) { ali@416: prim = atomic_action_new(ACTION_MOVE); ali@475: prim->args.uri = strdup(action->args.uri); ali@416: prim->args.u.move.dest = atomic_action_attic_tmpnam(atomic); ali@416: ali@416: atomic_action_free(action); ali@416: } else ali@416: prim = NULL; ali@416: ali@416: return prim ? atomic_action_do(atomic, prim) : NULL; ali@416: } ali@416: ali@416: static struct atomic_action * ali@416: atomic_action_create_dir(struct razor_atomic *atomic, ali@416: struct atomic_action *action) ali@416: { ali@416: mode_t mode; ali@475: struct razor_error **error; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) { ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } ali@416: ali@416: mode = action->args.u.create_dir.mode & (S_IRWXU | S_IRWXG | S_IRWXO); ali@416: ali@475: if (atomic->error) ali@475: error = NULL; ali@475: else ali@475: error = &atomic->error; ali@475: ali@475: if (!razor_uri_mkdir(action->args.uri, mode, error)) ali@450: return action; ali@416: ali@450: atomic_action_free(action); ali@450: return NULL; ali@416: } ali@416: ali@416: static struct atomic_action *atomic_action_rmdir(struct razor_atomic *atomic, ali@416: struct atomic_action *action) ali@416: { ali@416: if (razor_atomic_in_error_state(atomic)) { ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } ali@416: ali@475: if (rmdir(action->args.uri) < 0) { ali@423: if (!atomic->error) ali@475: atomic->error = razor_error_new_posix(action->args.uri); ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } else ali@416: return action; ali@416: } ali@416: ali@416: #if HAVE_SYMLINK ali@416: static struct atomic_action * ali@416: atomic_action_create_symlink(struct razor_atomic *atomic, ali@416: struct atomic_action *action) ali@416: { ali@416: int r; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) { ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } ali@416: ali@475: r = symlink(action->args.u.create_symlink.target, action->args.uri); ali@416: if (r < 0) { ali@423: if (!atomic->error) ali@475: atomic->error = razor_error_new_posix(action->args.uri); ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } ali@416: ali@416: return action; ali@416: } ali@416: ali@416: static struct atomic_action * ali@416: atomic_action_remove_symlink(struct razor_atomic *atomic, ali@416: struct atomic_action *action) ali@416: { ali@475: struct razor_error **error; ali@475: ali@416: if (razor_atomic_in_error_state(atomic)) { ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } ali@416: ali@475: if (atomic->error) ali@475: error = NULL; ali@475: else ali@475: error = &atomic->error; ali@475: ali@475: if (razor_uri_unlink(action->args.uri, error)) { ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } ali@416: ali@416: return action; ali@416: } ali@416: #endif ali@416: ali@416: static int ali@475: move_file(struct razor_atomic *atomic, const char *uri, const char *dest) ali@416: { ali@475: struct razor_error **error; ali@416: ali@475: if (atomic->error) ali@475: error = NULL; ali@475: else ali@475: error = &atomic->error; ali@416: ali@475: return razor_uri_move(uri, dest, error); ali@416: } ali@416: ali@416: static struct atomic_action * ali@416: atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action) ali@416: { ali@416: if (razor_atomic_in_error_state(atomic)) { ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } ali@416: ali@475: if (move_file(atomic, action->args.uri, action->args.u.move.dest)) { ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } ali@416: ali@416: return action; ali@416: } ali@416: ali@416: static struct atomic_action * ali@416: atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action) ali@416: { ali@416: if (razor_atomic_in_error_state(atomic)) { ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } ali@416: ali@475: if (move_file(atomic, action->args.u.move.dest, action->args.uri)) { ali@416: atomic_action_free(action); ali@416: return NULL; ali@416: } ali@416: ali@416: return action; ali@416: } ali@416: ali@416: static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic, ali@416: struct atomic_action *action) ali@416: { ali@416: switch(action->type) { ali@416: case ACTION_MAKE_DIRS: ali@416: action = atomic_action_make_dirs(atomic, action); ali@416: break; ali@416: case ACTION_REMOVE: ali@416: action = atomic_action_remove(atomic, action); ali@416: break; ali@416: case ACTION_CREATE_DIR: ali@416: action = atomic_action_create_dir(atomic, action); ali@416: break; ali@416: case ACTION_MOVE: ali@416: action = atomic_action_move(atomic, action); ali@416: break; ali@416: #if HAVE_SYMLINK ali@416: case ACTION_CREATE_SYMLINK: ali@416: action = atomic_action_create_symlink(atomic, action); ali@416: break; ali@416: #endif ali@416: } ali@416: return action; ali@416: } ali@416: ali@416: static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic, ali@416: struct atomic_action *action) ali@416: { ali@416: switch(action->type) { ali@416: case ACTION_MAKE_DIRS: ali@416: case ACTION_REMOVE: ali@416: /* Complex actions: should never happen */ ali@416: break; ali@416: case ACTION_CREATE_DIR: ali@416: action = atomic_action_rmdir(atomic, action); ali@416: break; ali@416: case ACTION_MOVE: ali@416: action = atomic_action_unmove(atomic, action); ali@416: break; ali@416: #if HAVE_SYMLINK ali@416: case ACTION_CREATE_SYMLINK: ali@416: action = atomic_action_remove_symlink(atomic, action); ali@416: break; ali@416: #endif ali@416: } ali@416: return action; ali@416: } ali@416: ali@416: /* ali@416: * Note that undo has no error checking. ali@416: */ ali@416: ali@416: void atomic_action_undo(struct razor_atomic *atomic, ali@416: struct atomic_action *actions) ali@416: { ali@416: struct atomic_action *a; ali@416: ali@416: atomic->in_undo = 1; ali@416: ali@416: while (actions) { ali@416: a = atomic_action_list_pop_head(&actions); ali@416: a = atomic_action_reverse(atomic, a); ali@416: atomic_action_free(a); ali@416: } ali@416: ali@416: atomic->in_undo = 0; ali@416: } ali@416: ali@416: struct atomic_action *atomic_action_do(struct razor_atomic *atomic, ali@416: struct atomic_action *actions) ali@416: { ali@416: struct atomic_action *done = NULL, *a; ali@416: ali@416: if (razor_atomic_in_error_state(atomic)) { ali@416: atomic_action_free(actions); ali@416: return NULL; ali@416: } ali@416: ali@416: while (actions) { ali@416: a = atomic_action_list_pop_head(&actions); ali@416: a = atomic_action_apply(atomic, a); ali@416: if (a) ali@416: done = atomic_action_list_concat(a, done); ali@416: if (razor_atomic_in_error_state(atomic)) { ali@416: atomic_action_undo(atomic, done); ali@416: done = NULL; ali@444: atomic_action_free(a); ali@416: } ali@416: } ali@416: ali@416: return done; ali@416: } ali@416: ali@416: #endif /* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */