librazor/atomic-emulate.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Jul 07 11:04:10 2016 +0100 (2016-07-07)
changeset 478 8e4bf84a7bb8
parent 461 e1b95d57dd54
permissions -rw-r--r--
Port KTM driver to URI-based API
     1 /*
     2  * Copyright (C) 2012, 2014, 2016  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 <stdio.h>
    25 #include <string.h>
    26 #include <unistd.h>
    27 #include <fcntl.h>
    28 #include "razor-internal.h"
    29 
    30 /*
    31  * Emulated atomic support
    32  *
    33  * This implementation is better than nothing, but is certainly not atomic.
    34  * It does have a couple of advantages over atomic-none:
    35  *	- If a file operation fails while a package is being installed we
    36  *	  have a good chance of being able to rollback the transaction to
    37  *	  a well-known state.
    38  *	- We behave similarly to atomic-ktm in that changes are not visible
    39  *	  on disk to non-atomic operations (eg., scripts) until the atomic
    40  *	  is committed. This makes the testsuite more likely to pick up
    41  *	  problems that would otherwise only be found when using razor on
    42  *	  an MS-Windows system which supports KTM.
    43  */
    44 
    45 #ifndef O_BINARY
    46 #define O_BINARY	0
    47 #endif
    48 
    49 static void recursive_remove(const char *directory)
    50 {
    51 	void *dp;
    52 	char *name, *buf;
    53 
    54 	dp = razor_uri_opendir(directory, NULL);
    55 
    56 	if (dp) {
    57 		while((name = razor_uri_readdir(dp, NULL))) {
    58 			buf = razor_concat(directory, "/", name, NULL);
    59 			free(name);
    60 			if (razor_uri_unlink(buf, NULL) < 0)
    61 				recursive_remove(buf);
    62 			free(buf);
    63 		}
    64 
    65 		razor_uri_closedir(dp, NULL);
    66 	}
    67 
    68 	rmdir(directory);
    69 }
    70 
    71 RAZOR_EXPORT struct razor_atomic *razor_atomic_open(const char *description)
    72 {
    73 	struct razor_atomic *atomic;
    74 
    75 	atomic = zalloc(sizeof *atomic);
    76 
    77 	atomic->description = strdup(description);
    78 
    79 	return atomic;
    80 }
    81 
    82 RAZOR_EXPORT int razor_atomic_commit(struct razor_atomic *atomic)
    83 {
    84 	struct atomic_action *actions;
    85 
    86 	if (razor_atomic_in_error_state(atomic))
    87 		return -1;
    88 
    89 	if (atomic->actions) {
    90 		actions = atomic_action_list_reverse(atomic->actions);
    91 		atomic->actions = NULL;
    92 		actions = atomic_action_do(atomic, actions);
    93 		atomic_action_free(actions);
    94 	}
    95 
    96 	if (atomic->toplevel) {
    97 		recursive_remove(atomic->toplevel);
    98 		free(atomic->toplevel);
    99 		atomic->toplevel = NULL;
   100 	}
   101 
   102 	return razor_atomic_in_error_state(atomic);
   103 }
   104 
   105 RAZOR_EXPORT void razor_atomic_destroy(struct razor_atomic *atomic)
   106 {
   107 	if (atomic->actions) {
   108 		atomic_action_free(atomic->actions);
   109 		atomic->actions = NULL;
   110 	}
   111 
   112 	if (atomic->toplevel) {
   113 		recursive_remove(atomic->toplevel);
   114 		free(atomic->toplevel);
   115 		atomic->toplevel = NULL;
   116 	}
   117 
   118 	if (atomic->error)
   119 		razor_error_free(atomic->error);
   120 
   121 	free(atomic->description);
   122 
   123 	free(atomic);
   124 }
   125 
   126 /*
   127  * We need a toplevel directory in which to hold temporary files
   128  * before they are committed. Since we can generally assume that
   129  * we have write permissions anywhere on the filesystem in
   130  * question, the best location is at the relevant mount point.
   131  * The most common case where this assumption fails is when
   132  * testing, when the current directory is a good choice.
   133  */
   134 
   135 static int
   136 razor_atomic_set_toplevel_from_uri(struct razor_atomic *atomic,
   137 				   const char *uri)
   138 {
   139 	if (razor_atomic_in_error_state(atomic))
   140 		return -1;
   141 
   142 	if (atomic->toplevel)
   143 		return 0;
   144 
   145 #ifdef MSWIN_API
   146 	atomic->toplevel = razor_uri_mkdtemp_near(uri, "atomic-XXXXXX",
   147 						   &atomic->error);
   148 #else
   149 	atomic->toplevel = razor_uri_mkdtemp_near(uri, ".atomic-XXXXXX",
   150 						   &atomic->error);
   151 #endif
   152 
   153 	return !atomic->toplevel;
   154 }
   155 
   156 /*
   157  * If root_uri is empty, then uri can be a URI (but not a relative-ref; ie.,
   158  * a scheme must be specified). If root_uri is non-empty, then it must be a
   159  * URI (but not a relative-ref) and uri must be a non-empty path.
   160  */
   161 RAZOR_EXPORT int
   162 razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root_uri,
   163 		       const char *uri)
   164 {
   165 	struct atomic_action *a;
   166 	struct razor_uri ru;
   167 	struct razor_error *tmp_error = NULL;
   168 	char *fakeroot = NULL;
   169 
   170 	razor_atomic_set_toplevel_from_uri(atomic, *root_uri ? root_uri : uri);
   171 
   172 	if (razor_atomic_in_error_state(atomic))
   173 		return -1;
   174 
   175 	if (!*root_uri) {
   176 		if (razor_uri_parse(&ru, uri, &tmp_error)) {
   177 			razor_atomic_propagate_error(atomic, tmp_error, NULL);
   178 			return -1;
   179 		}
   180 		if (*ru.path == '/') {
   181 			free(ru.path);
   182 			ru.path = strdup("/");
   183 		} else {
   184 			free(ru.path);
   185 			ru.path = strdup("");
   186 		}
   187 		fakeroot = razor_uri_recompose(&ru);
   188 		razor_uri_destroy(&ru);
   189 		root_uri = fakeroot;
   190 		uri += strlen(fakeroot);
   191 	}
   192 
   193 	a = atomic_action_new(ACTION_MAKE_DIRS);
   194 	a->args.uri = strdup(uri);
   195 	a->args.u.make_dirs.root = strdup(root_uri);
   196 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   197 
   198 	if (fakeroot)
   199 		free(fakeroot);
   200 
   201 	return 0;
   202 }
   203 
   204 RAZOR_EXPORT int
   205 razor_atomic_remove(struct razor_atomic *atomic, const char *uri)
   206 {
   207 	struct atomic_action *a;
   208 
   209 	razor_atomic_set_toplevel_from_uri(atomic, uri);
   210 
   211 	if (razor_atomic_in_error_state(atomic))
   212 		return -1;
   213 
   214 	a = atomic_action_new(ACTION_REMOVE);
   215 	a->args.uri = strdup(uri);
   216 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   217 
   218 	return 0;
   219 }
   220 
   221 RAZOR_EXPORT int
   222 razor_atomic_rename_file(struct razor_atomic *atomic, const char *old_uri,
   223 			 const char *new_uri)
   224 {
   225 	struct atomic_action *a;
   226 
   227 	razor_atomic_set_toplevel_from_uri(atomic, new_uri);
   228 
   229 	if (razor_atomic_in_error_state(atomic))
   230 		return -1;
   231 
   232 	a = atomic_action_new(ACTION_MOVE);
   233 	a->args.uri = strdup(old_uri);
   234 	a->args.u.move.dest = strdup(new_uri);
   235 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   236 
   237 	return 0;
   238 }
   239 
   240 RAZOR_EXPORT int
   241 razor_atomic_create_dir(struct razor_atomic *atomic, const char *uri,
   242 			mode_t mode)
   243 {
   244 	struct atomic_action *a;
   245 
   246 	razor_atomic_set_toplevel_from_uri(atomic, uri);
   247 
   248 	if (razor_atomic_in_error_state(atomic))
   249 		return -1;
   250 
   251 	a = atomic_action_new(ACTION_CREATE_DIR);
   252 	a->args.uri = strdup(uri);
   253 	a->args.u.create_dir.mode = mode;
   254 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   255 
   256 	return 0;
   257 }
   258 
   259 RAZOR_EXPORT int
   260 razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
   261 			    const char *uri)
   262 {
   263 #if HAVE_SYMLINK
   264 	struct atomic_action *a;
   265 
   266 	razor_atomic_set_toplevel_from_uri(atomic, uri);
   267 #endif
   268 
   269 	if (razor_atomic_in_error_state(atomic))
   270 		return -1;
   271 
   272 #if HAVE_SYMLINK
   273 	a = atomic_action_new(ACTION_CREATE_SYMLINK);
   274 	a->args.uri = strdup(uri);
   275 	a->args.u.create_symlink.target = strdup(target);
   276 	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
   277 
   278 	return 0;
   279 #else
   280 	atomic->error = razor_error_new_str(RAZOR_POSIX_ERROR, ENOSYS, NULL,
   281 					    "Symbolic links not supported "
   282 					    "on this platform");
   283 
   284 	return -1;
   285 #endif
   286 }
   287 
   288 RAZOR_EXPORT int
   289 razor_atomic_create_file(struct razor_atomic *atomic, const char *uri,
   290                          mode_t mode)
   291 {
   292 	int fd;
   293 	struct atomic_action *a;
   294 	char *tmpnam;
   295 
   296 	razor_atomic_set_toplevel_from_uri(atomic, uri);
   297 
   298 	if (razor_atomic_in_error_state(atomic))
   299 		return -1;
   300 
   301 	tmpnam = atomic_action_attic_tmpnam(atomic);
   302 	fd = razor_uri_open(tmpnam, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
   303 			    mode & (S_IRWXU | S_IRWXG | S_IRWXO),
   304 			    &atomic->error);
   305 
   306 	if (fd >= 0) {
   307 		a = atomic_action_new(ACTION_MOVE);
   308 		a->args.uri = tmpnam;
   309 		a->args.u.move.dest = strdup(uri);
   310 		atomic->actions = atomic_action_list_prepend(atomic->actions,
   311 							     a);
   312 	}
   313 
   314 	return fd;
   315 }
   316 
   317 #endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */