librazor/atomic-emulate.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Feb 11 09:34:40 2012 +0000 (2012-02-11)
changeset 421 408c66ad463d
child 423 6112bcc5d1cf
permissions -rw-r--r--
Fix object management in error paths
     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 */