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