librazor/atomic-actions.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Jul 08 15:54:49 2016 +0100 (2016-07-08)
changeset 482 6a8a57779674
parent 471 a3e5e3eaf224
permissions -rw-r--r--
Release 0.6.3.101
     1 /*
     2  * Copyright (C) 2012, 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 <limits.h>
    27 #include <errno.h>
    28 #include <assert.h>
    29 #include "razor-internal.h"
    30 
    31 char *atomic_action_attic_tmpnam(struct razor_atomic *atomic)
    32 {
    33 	char filename[17];
    34 	sprintf(filename,"%03X",atomic->next_file_tag++);
    35 	return razor_concat(atomic->toplevel, "/", filename, NULL);
    36 }
    37 
    38 static struct atomic_action *
    39 atomic_action_list_pop_head(struct atomic_action **list)
    40 {
    41 	struct atomic_action *head;
    42 
    43 	head = *list;
    44 	if (head) {
    45 		*list = head->next;
    46 		head->next = NULL;
    47 	}
    48 
    49 	return head;
    50 }
    51 
    52 struct atomic_action *
    53 atomic_action_list_prepend(struct atomic_action *list,
    54 			   struct atomic_action *action)
    55 {
    56 	assert(action->next == NULL);
    57 
    58 	action->next=list;
    59 
    60 	return action;
    61 }
    62 
    63 static struct atomic_action *
    64 atomic_action_list_concat(struct atomic_action *list1,
    65 			  struct atomic_action *list2)
    66 {
    67 	struct atomic_action *action;
    68 
    69 	if (!list1)
    70 		return list2;
    71 
    72 	for(action=list1;action->next;action=action->next)
    73 		;
    74 
    75 	action->next=list2;
    76 
    77 	return list1;
    78 }
    79 
    80 void atomic_action_free(struct atomic_action *action)
    81 {
    82 	struct atomic_action *a;
    83 
    84 	while(action) {
    85 		a = atomic_action_list_pop_head(&action);
    86 
    87 		free(a->args.uri);
    88 
    89 		switch(a->type) {
    90 		case ACTION_MAKE_DIRS:
    91 			free(a->args.u.make_dirs.root);
    92 			break;
    93 		case ACTION_MOVE:
    94 			free(a->args.u.move.dest);
    95 			break;
    96 #if HAVE_SYMLINK
    97 		case ACTION_CREATE_SYMLINK:
    98 			free(a->args.u.create_symlink.target);
    99 			break;
   100 #endif
   101 		case ACTION_REMOVE:
   102 		case ACTION_CREATE_DIR:
   103 			break;
   104 		}
   105 
   106 		free(a);
   107 	}
   108 }
   109 
   110 struct atomic_action *atomic_action_new(enum atomic_action_type type)
   111 {
   112 	struct atomic_action *action;
   113 
   114 	action = zalloc(sizeof *action);
   115 	action->type = type;
   116 
   117 	return action;
   118 }
   119 
   120 struct atomic_action *atomic_action_list_reverse(struct atomic_action *list)
   121 {
   122 	struct atomic_action *prev = NULL, *next;
   123 
   124 	while(list) {
   125 		next = list->next;
   126 		list->next = prev;
   127 		prev = list;
   128 		list = next;
   129 	}
   130 
   131 	return prev;
   132 }
   133 
   134 /*
   135  * All action_ functions take 1 action and return a list of primitive
   136  * actions they took (such that the first action in the list is the
   137  * last action they took). Primitive actions are always reversable.
   138  *
   139  * On failure, an error should be set on the atomic object (if it is
   140  * not already set), the action freed and NULL returned.
   141  *
   142  * Whether they succeed or fail, they take ownership of the passed
   143  * action and should thus either free it or return it back to their
   144  * caller as appropriate.
   145  *
   146  * A NULL return means that nothing was done which may, or may not,
   147  * indicate an error.
   148  */
   149 
   150 static struct atomic_action *
   151 atomic_action_make_dirs(struct razor_atomic *atomic,
   152 			struct atomic_action *action)
   153 {
   154 	char buffer[PATH_MAX], *p;
   155 	const char *slash, *next;
   156 	struct atomic_action *primitives = NULL;
   157 	struct atomic_action *prim;
   158 
   159 	if (razor_atomic_in_error_state(atomic)) {
   160 		atomic_action_free(action);
   161 		return NULL;
   162 	}
   163 
   164 	strcpy(buffer, action->args.u.make_dirs.root);
   165 	p = buffer + strlen(buffer);
   166 	slash = action->args.uri;
   167 	if (p > buffer && p[-1] != ':' && p[-1] != '/' && *slash != '/')
   168 		*p++ = '/';
   169 	for (; *slash != '\0'; slash = next) {
   170 		next = strchr(slash + 1, '/');
   171 		if (next == NULL)
   172 			break;
   173 
   174 		memcpy(p, slash, next - slash);
   175 		p += next - slash;
   176 		*p = '\0';
   177 
   178 		if (razor_valid_root_name(buffer))
   179 			continue;
   180 
   181 		prim = atomic_action_new(ACTION_CREATE_DIR);
   182 		prim->args.uri = strdup(buffer);
   183 		prim->args.u.create_dir.mode = S_IRWXU | S_IRWXG | S_IRWXO;
   184 		primitives = atomic_action_list_prepend(primitives, prim);
   185 	}
   186 	primitives = atomic_action_list_reverse(primitives);
   187 
   188 	atomic_action_free(action);
   189 
   190 	return atomic_action_do(atomic, primitives);
   191 }
   192 
   193 static struct atomic_action *
   194 atomic_action_remove(struct razor_atomic *atomic, struct atomic_action *action)
   195 {
   196 	void *dir;
   197 	char *name;
   198 	struct atomic_action *prim;
   199 
   200 	if (razor_atomic_in_error_state(atomic)) {
   201 		atomic_action_free(action);
   202 		return NULL;
   203 	}
   204 
   205 	/*
   206 	 * Non-empty directories should NOT be removed
   207 	 */
   208 	dir = razor_uri_opendir(action->args.uri, NULL);
   209 	if (dir) {
   210 		name = razor_uri_readdir(dir, NULL);
   211 		razor_uri_closedir(dir, NULL);
   212 		if (name) {
   213 			free(name);
   214 			atomic_action_free(action);
   215 			action = NULL;
   216 		}
   217 	}
   218 
   219 	if (action) {
   220 		prim = atomic_action_new(ACTION_MOVE);
   221 		prim->args.uri = strdup(action->args.uri);
   222 		prim->args.u.move.dest = atomic_action_attic_tmpnam(atomic);
   223 
   224 		atomic_action_free(action);
   225 	} else
   226 		prim = NULL;
   227 
   228 	return prim ? atomic_action_do(atomic, prim) : NULL;
   229 }
   230 
   231 static struct atomic_action *
   232 atomic_action_create_dir(struct razor_atomic *atomic,
   233 			 struct atomic_action *action)
   234 {
   235 	mode_t mode;
   236 	struct razor_error **error;
   237 
   238 	if (razor_atomic_in_error_state(atomic)) {
   239 		atomic_action_free(action);
   240 		return NULL;
   241 	}
   242 
   243 	mode = action->args.u.create_dir.mode & (S_IRWXU | S_IRWXG | S_IRWXO);
   244 
   245 	if (atomic->error)
   246 		error = NULL;
   247 	else
   248 		error = &atomic->error;
   249 
   250 	if (!razor_uri_mkdir(action->args.uri, mode, error))
   251 		return action;
   252 
   253 	atomic_action_free(action);
   254 	return NULL;
   255 }
   256 
   257 static struct atomic_action *atomic_action_rmdir(struct razor_atomic *atomic,
   258 						 struct atomic_action *action)
   259 {
   260 	if (razor_atomic_in_error_state(atomic)) {
   261 		atomic_action_free(action);
   262 		return NULL;
   263 	}
   264 
   265 	if (rmdir(action->args.uri) < 0) {
   266 		if (!atomic->error)
   267 			atomic->error = razor_error_new_posix(action->args.uri);
   268 		atomic_action_free(action);
   269 		return NULL;
   270 	} else
   271 		return action;
   272 }
   273 
   274 #if HAVE_SYMLINK
   275 static struct atomic_action *
   276 atomic_action_create_symlink(struct razor_atomic *atomic,
   277 			     struct atomic_action *action)
   278 {
   279 	int r;
   280 
   281 	if (razor_atomic_in_error_state(atomic)) {
   282 		atomic_action_free(action);
   283 		return NULL;
   284 	}
   285 
   286 	r = symlink(action->args.u.create_symlink.target, action->args.uri);
   287 	if (r < 0) {
   288 		if (!atomic->error)
   289 			atomic->error = razor_error_new_posix(action->args.uri);
   290 		atomic_action_free(action);
   291 		return NULL;
   292 	}
   293 
   294 	return action;
   295 }
   296 
   297 static struct atomic_action *
   298 atomic_action_remove_symlink(struct razor_atomic *atomic,
   299 			     struct atomic_action *action)
   300 {
   301 	struct razor_error **error;
   302 
   303 	if (razor_atomic_in_error_state(atomic)) {
   304 		atomic_action_free(action);
   305 		return NULL;
   306 	}
   307 
   308 	if (atomic->error)
   309 		error = NULL;
   310 	else
   311 		error = &atomic->error;
   312 
   313 	if (razor_uri_unlink(action->args.uri, error)) {
   314 		atomic_action_free(action);
   315 		return NULL;
   316 	}
   317 
   318 	return action;
   319 }
   320 #endif
   321 
   322 static int
   323 move_file(struct razor_atomic *atomic, const char *uri, const char *dest)
   324 {
   325 	struct razor_error **error;
   326 
   327 	if (atomic->error)
   328 		error = NULL;
   329 	else
   330 		error = &atomic->error;
   331 
   332 	return razor_uri_move(uri, dest, error);
   333 }
   334 
   335 static struct atomic_action *
   336 atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action)
   337 {
   338 	if (razor_atomic_in_error_state(atomic)) {
   339 		atomic_action_free(action);
   340 		return NULL;
   341 	}
   342 
   343 	if (move_file(atomic, action->args.uri, action->args.u.move.dest)) {
   344 		atomic_action_free(action);
   345 		return NULL;
   346 	}
   347 
   348 	return action;
   349 }
   350 
   351 static struct atomic_action *
   352 atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action)
   353 {
   354 	if (razor_atomic_in_error_state(atomic)) {
   355 		atomic_action_free(action);
   356 		return NULL;
   357 	}
   358 
   359 	if (move_file(atomic, action->args.u.move.dest, action->args.uri)) {
   360 		atomic_action_free(action);
   361 		return NULL;
   362 	}
   363 
   364 	return action;
   365 }
   366 
   367 static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic,
   368 						 struct atomic_action *action)
   369 {
   370 	switch(action->type) {
   371 	case ACTION_MAKE_DIRS:
   372 		action = atomic_action_make_dirs(atomic, action);
   373 		break;
   374 	case ACTION_REMOVE:
   375 		action = atomic_action_remove(atomic, action);
   376 		break;
   377 	case ACTION_CREATE_DIR:
   378 		action = atomic_action_create_dir(atomic, action);
   379 		break;
   380 	case ACTION_MOVE:
   381 		action = atomic_action_move(atomic, action);
   382 		break;
   383 #if HAVE_SYMLINK
   384 	case ACTION_CREATE_SYMLINK:
   385 		action = atomic_action_create_symlink(atomic, action);
   386 		break;
   387 #endif
   388 	}
   389 	return action;
   390 }
   391 
   392 static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic,
   393 						   struct atomic_action *action)
   394 {
   395 	switch(action->type) {
   396 	case ACTION_MAKE_DIRS:
   397 	case ACTION_REMOVE:
   398 		/* Complex actions: should never happen */
   399 		break;
   400 	case ACTION_CREATE_DIR:
   401 		action = atomic_action_rmdir(atomic, action);
   402 		break;
   403 	case ACTION_MOVE:
   404 		action = atomic_action_unmove(atomic, action);
   405 		break;
   406 #if HAVE_SYMLINK
   407 	case ACTION_CREATE_SYMLINK:
   408 		action = atomic_action_remove_symlink(atomic, action);
   409 		break;
   410 #endif
   411 	}
   412 	return action;
   413 }
   414 
   415 /*
   416  * Note that undo has no error checking.
   417  */
   418 
   419 void atomic_action_undo(struct razor_atomic *atomic,
   420 			struct atomic_action *actions)
   421 {
   422 	struct atomic_action *a;
   423 
   424 	atomic->in_undo = 1;
   425 
   426 	while (actions) {
   427 		a = atomic_action_list_pop_head(&actions);
   428 		a = atomic_action_reverse(atomic, a);
   429 		atomic_action_free(a);
   430 	}
   431 
   432 	atomic->in_undo = 0;
   433 }
   434 
   435 struct atomic_action *atomic_action_do(struct razor_atomic *atomic,
   436 				       struct atomic_action *actions)
   437 {
   438 	struct atomic_action *done = NULL, *a;
   439 
   440 	if (razor_atomic_in_error_state(atomic)) {
   441 		atomic_action_free(actions);
   442 		return NULL;
   443 	}
   444 
   445 	while (actions) {
   446 		a = atomic_action_list_pop_head(&actions);
   447 		a = atomic_action_apply(atomic, a);
   448 		if (a)
   449 			done = atomic_action_list_concat(a, done);
   450 		if (razor_atomic_in_error_state(atomic)) {
   451 			atomic_action_undo(atomic, done);
   452 			done = NULL;
   453 			atomic_action_free(a);
   454 		}
   455 	}
   456 
   457 	return done;
   458 }
   459 
   460 #endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */