librazor/atomic-emulate.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Feb 09 20:45:27 2012 +0000 (2012-02-09)
changeset 418 33b825d3128d
child 423 6112bcc5d1cf
permissions -rw-r--r--
Add transaction barriers
These allow packages to be installed and removed which have scripts
that depend on each other when atomic transactions are involved.
Note that yum supports pre, but not other requires flags. post will
need similar support to the post scripts themselves pulling in the
requires flags from the rpms. Likewise preun and postun will need
similar handling to those scrips since the requires flags will need
to be stored in the razor database.
     1 /*
     2  * Copyright (C) 2012  J. Ali Harlow <ali@juiblex.co.uk>
     3  *
     4  * This program is free software; you can redistribute it and/or modify
     5  * it under the terms of the GNU General Public License as published by
     6  * the Free Software Foundation; either version 2 of the License, or
     7  * (at your option) any later version.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License along
    15  * with this program; if not, write to the Free Software Foundation, Inc.,
    16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    17  */
    18 
    19 #include "config.h"
    20 
    21 #if ENABLE_ATOMIC && !HAVE_WINDOWS_KTM
    22 
    23 #include <stdlib.h>
    24 #include <string.h>
    25 #include <unistd.h>
    26 #include <sys/types.h>
    27 #include <sys/stat.h>
    28 #include <fcntl.h>
    29 #include <dirent.h>
    30 #include <errno.h>
    31 #include "razor-internal.h"
    32 
    33 /*
    34  * Emulated atomic support
    35  *
    36  * This implementation is better than nothing, but is certainly not atomic.
    37  * It does have a couple of advantages over atomic-none:
    38  *	- If a file operation fails while a package is being installed we
    39  *	  have a good chance of being able to rollback the transaction to
    40  *	  a well-known state.
    41  *	- We behave similarly to atomic-ktm in that changes are not visible
    42  *	  on disk to non-atomic operations (eg., scripts) until the atomic
    43  *	  is committed. This makes the testsuite more likely to pick up
    44  *	  problems that would otherwise only be found when using razor on
    45  *	  an MS-Windows system which supports KTM.
    46  */
    47 
    48 #ifndef O_BINARY
    49 #define O_BINARY	0
    50 #endif
    51 
    52 static void recursive_remove(const char *directory)
    53 {
    54 	DIR *dp;
    55 	struct dirent *dirp;
    56 	char *buf;
    57 
    58 	dp = opendir(directory);
    59 	while((dirp = readdir(dp))) {
    60 		if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) {
    61 			buf = malloc(strlen(directory) + strlen(dirp->d_name)
    62 				     + 2);
    63 			sprintf(buf, "%s/%s", directory, dirp->d_name);
    64 			if (remove(buf) < 0)
    65 				recursive_remove(buf);
    66 			free(buf);
    67 		}
    68 	}
    69 
    70 	rmdir(directory);
    71 }
    72 
    73 RAZOR_EXPORT struct razor_atomic *razor_atomic_open(const char *description)
    74 {
    75 	struct razor_atomic *atomic;
    76 
    77 	atomic = zalloc(sizeof *atomic);
    78 
    79 	atomic->toplevel = strdup(".atomic-XXXXXX");
    80 	if (!mkdtemp(atomic->toplevel)) {
    81 		free(atomic->toplevel);
    82 		free(atomic);
    83 		return NULL;
    84 	}
    85 
    86 	atomic->description = strdup(description);
    87 
    88 	return atomic;
    89 }
    90 
    91 RAZOR_EXPORT int razor_atomic_commit(struct razor_atomic *atomic)
    92 {
    93 	struct atomic_action *actions;
    94 
    95 	if (razor_atomic_in_error_state(atomic))
    96 		return -1;
    97 
    98 	if (atomic->actions) {
    99 		actions = atomic_action_list_reverse(atomic->actions);
   100 		atomic->actions = NULL;
   101 		actions = atomic_action_do(atomic, actions);
   102 		atomic_action_free(actions);
   103 	}
   104 
   105 	if (atomic->toplevel) {
   106 		recursive_remove(atomic->toplevel);
   107 		free(atomic->toplevel);
   108 		atomic->toplevel = NULL;
   109 	}
   110 
   111 	return !!atomic->error_str;
   112 }
   113 
   114 RAZOR_EXPORT void razor_atomic_destroy(struct razor_atomic *atomic)
   115 {
   116 	if (atomic->toplevel) {
   117 		recursive_remove(atomic->toplevel);
   118 		free(atomic->toplevel);
   119 		atomic->toplevel = NULL;
   120 	}
   121 
   122 	free(atomic->error_path);
   123 	free(atomic->error_str);
   124 	free(atomic->error_msg);
   125 	free(atomic);
   126 }
   127 
   128 RAZOR_EXPORT int
   129 razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
   130 		       const char *path)
   131 {
   132 	struct atomic_action *a;
   133 
   134 	if (razor_atomic_in_error_state(atomic))
   135 		return -1;
   136 
   137 	a = atomic_action_new(ACTION_MAKE_DIRS);
   138 	a->args.path = strdup(path);
   139 	a->args.u.make_dirs.root = strdup(root);
   140 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   141 
   142 	return 0;
   143 }
   144 
   145 RAZOR_EXPORT int
   146 razor_atomic_remove(struct razor_atomic *atomic, const char *path)
   147 {
   148 	struct atomic_action *a;
   149 
   150 	if (razor_atomic_in_error_state(atomic))
   151 		return -1;
   152 
   153 	a = atomic_action_new(ACTION_REMOVE);
   154 	a->args.path = strdup(path);
   155 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   156 
   157 	return 0;
   158 }
   159 
   160 RAZOR_EXPORT int
   161 razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
   162 			 const char *newpath)
   163 {
   164 	struct atomic_action *a;
   165 
   166 	if (razor_atomic_in_error_state(atomic))
   167 		return -1;
   168 
   169 	a = atomic_action_new(ACTION_MOVE);
   170 	a->args.path = strdup(oldpath);
   171 	a->args.u.move.dest = strdup(newpath);
   172 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   173 
   174 	return 0;
   175 }
   176 
   177 RAZOR_EXPORT int
   178 razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
   179 			mode_t mode)
   180 {
   181 	struct atomic_action *a;
   182 
   183 	if (razor_atomic_in_error_state(atomic))
   184 		return -1;
   185 
   186 	a = atomic_action_new(ACTION_CREATE_DIR);
   187 	a->args.path = strdup(dirname);
   188 	a->args.u.create_dir.mode = mode;
   189 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   190 
   191 	return 0;
   192 }
   193 
   194 RAZOR_EXPORT int
   195 razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
   196 			    const char *path)
   197 {
   198 #if HAVE_SYMLINK
   199 	struct atomic_action *a;
   200 #endif
   201 
   202 	if (razor_atomic_in_error_state(atomic))
   203 		return -1;
   204 
   205 #if HAVE_SYMLINK
   206 	a = atomic_action_new(ACTION_CREATE_SYMLINK);
   207 	a->args.path = strdup(path);
   208 	a->args.u.create_symlink.target = strdup(target);
   209 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   210 
   211 	return 0;
   212 #else
   213 	razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported "
   214 						 "on this platform");
   215 
   216 	return -1;
   217 #endif
   218 }
   219 
   220 RAZOR_EXPORT int
   221 razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
   222                          mode_t mode)
   223 {
   224 	int fd;
   225 	struct atomic_action *a;
   226 	char *tmpnam;
   227 
   228 	if (razor_atomic_in_error_state(atomic))
   229 		return -1;
   230 
   231 	atomic->error_path = strdup(filename);
   232 	tmpnam = atomic_action_attic_tmpnam(atomic);
   233 	fd = open(tmpnam, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
   234 		  mode & (S_IRWXU | S_IRWXG | S_IRWXO));
   235 
   236 	if (fd == -1)
   237 		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
   238 	else {
   239 		a = atomic_action_new(ACTION_MOVE);
   240 		a->args.path = tmpnam;
   241 		a->args.u.move.dest = strdup(filename);
   242 		atomic->actions = atomic_action_list_prepend(atomic->actions,
   243 							     a);
   244 	}
   245 
   246 	return fd;
   247 }
   248 
   249 #endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */