librazor/atomic-actions.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Oct 17 09:57:19 2014 +0100 (2014-10-17)
changeset 456 bae5adee8c8c
parent 447 0a5e583393e1
child 458 3f841a46eab5
permissions -rw-r--r--
Add facility to specify razor command when running tests.

This makes it easy to run under eg., valgrind as:

./details "libtool --mode=execute valgrind --leak-check=yes ../src/razor"
     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 	struct stat buf;
   251 
   252 	if (razor_atomic_in_error_state(atomic)) {
   253 		atomic_action_free(action);
   254 		return NULL;
   255 	}
   256 
   257 	mode = action->args.u.create_dir.mode & (S_IRWXU | S_IRWXG | S_IRWXO);
   258 
   259 	if (!mkdir(action->args.path, mode))
   260 		return action;
   261 
   262 	if (errno != EEXIST || stat(action->args.path, &buf)) {
   263 		if (!atomic->error)
   264 			atomic->error = razor_error_new_posix(action->args.path);
   265 		atomic_action_free(action);
   266 		return NULL;
   267 	}
   268 
   269 	if (!S_ISDIR(buf.st_mode) && !atomic->error)
   270 		atomic->error = razor_error_new_str(RAZOR_POSIX_ERROR, EEXIST,
   271 						    action->args.path,
   272 						    "Not a directory");
   273 
   274 	atomic_action_free(action);
   275 	return NULL;
   276 }
   277 
   278 static struct atomic_action *atomic_action_rmdir(struct razor_atomic *atomic,
   279 						 struct atomic_action *action)
   280 {
   281 	if (razor_atomic_in_error_state(atomic)) {
   282 		atomic_action_free(action);
   283 		return NULL;
   284 	}
   285 
   286 	if (rmdir(action->args.path) < 0) {
   287 		if (!atomic->error)
   288 			atomic->error = razor_error_new_posix(action->args.path);
   289 		atomic_action_free(action);
   290 		return NULL;
   291 	} else
   292 		return action;
   293 }
   294 
   295 #if HAVE_SYMLINK
   296 static struct atomic_action *
   297 atomic_action_create_symlink(struct razor_atomic *atomic,
   298 			     struct atomic_action *action)
   299 {
   300 	int r;
   301 
   302 	if (razor_atomic_in_error_state(atomic)) {
   303 		atomic_action_free(action);
   304 		return NULL;
   305 	}
   306 
   307 	r = symlink(action->args.u.create_symlink.target, action->args.path);
   308 	if (r < 0) {
   309 		if (!atomic->error)
   310 			atomic->error = razor_error_new_posix(action->args.path);
   311 		atomic_action_free(action);
   312 		return NULL;
   313 	}
   314 
   315 	return action;
   316 }
   317 
   318 static struct atomic_action *
   319 atomic_action_remove_symlink(struct razor_atomic *atomic,
   320 			     struct atomic_action *action)
   321 {
   322 	if (razor_atomic_in_error_state(atomic)) {
   323 		atomic_action_free(action);
   324 		return NULL;
   325 	}
   326 
   327 	if (unlink(action->args.path) < 0) {
   328 		if (!atomic->error)
   329 			atomic->error = razor_error_new_posix(action->args.path);
   330 		atomic_action_free(action);
   331 		return NULL;
   332 	}
   333 
   334 	return action;
   335 }
   336 #endif
   337 
   338 static int
   339 move_file(struct razor_atomic *atomic, const char *path, const char *dest)
   340 {
   341 #ifdef MSWIN_API
   342 	wchar_t *oldbuf, *newbuf;
   343 	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
   344 
   345 	newbuf = razor_utf8_to_utf16(dest, -1);
   346 	oldbuf = razor_utf8_to_utf16(path, -1);
   347 
   348 	/*
   349 	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
   350 	 * cover every case we care about _except_ replacing an empty
   351 	 * directory with a file. Calling RemoveDirectory() will deal
   352 	 * with this case while having no effect in all other cases.
   353 	 */
   354 	(void)RemoveDirectoryW(newbuf);
   355 
   356 	if (!MoveFileExW(oldbuf, newbuf, flags)) {
   357 		if (!atomic->error)
   358 			atomic->error = razor_error_new_mswin(newbuf,
   359 							      GetLastError());
   360 		return -1;
   361 	}
   362 
   363 	free(newbuf);
   364 	free(oldbuf);
   365 #else
   366 	if (rename(path, dest)) {
   367 		if (!atomic->error)
   368 			atomic->error = razor_error_new_posix(dest);
   369 		return -1;
   370 	}
   371 #endif
   372 
   373 	return 0;
   374 }
   375 
   376 static struct atomic_action *
   377 atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action)
   378 {
   379 	if (razor_atomic_in_error_state(atomic)) {
   380 		atomic_action_free(action);
   381 		return NULL;
   382 	}
   383 
   384 	if (move_file(atomic, action->args.path, action->args.u.move.dest)) {
   385 		atomic_action_free(action);
   386 		return NULL;
   387 	}
   388 
   389 	return action;
   390 }
   391 
   392 static struct atomic_action *
   393 atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action)
   394 {
   395 	if (razor_atomic_in_error_state(atomic)) {
   396 		atomic_action_free(action);
   397 		return NULL;
   398 	}
   399 
   400 	if (move_file(atomic, action->args.u.move.dest, action->args.path)) {
   401 		atomic_action_free(action);
   402 		return NULL;
   403 	}
   404 
   405 	return action;
   406 }
   407 
   408 static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic,
   409 						 struct atomic_action *action)
   410 {
   411 	switch(action->type) {
   412 	case ACTION_MAKE_DIRS:
   413 		action = atomic_action_make_dirs(atomic, action);
   414 		break;
   415 	case ACTION_REMOVE:
   416 		action = atomic_action_remove(atomic, action);
   417 		break;
   418 	case ACTION_CREATE_DIR:
   419 		action = atomic_action_create_dir(atomic, action);
   420 		break;
   421 	case ACTION_MOVE:
   422 		action = atomic_action_move(atomic, action);
   423 		break;
   424 #if HAVE_SYMLINK
   425 	case ACTION_CREATE_SYMLINK:
   426 		action = atomic_action_create_symlink(atomic, action);
   427 		break;
   428 #endif
   429 	}
   430 	return action;
   431 }
   432 
   433 static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic,
   434 						   struct atomic_action *action)
   435 {
   436 	switch(action->type) {
   437 	case ACTION_MAKE_DIRS:
   438 	case ACTION_REMOVE:
   439 		/* Complex actions: should never happen */
   440 		break;
   441 	case ACTION_CREATE_DIR:
   442 		action = atomic_action_rmdir(atomic, action);
   443 		break;
   444 	case ACTION_MOVE:
   445 		action = atomic_action_unmove(atomic, action);
   446 		break;
   447 #if HAVE_SYMLINK
   448 	case ACTION_CREATE_SYMLINK:
   449 		action = atomic_action_remove_symlink(atomic, action);
   450 		break;
   451 #endif
   452 	}
   453 	return action;
   454 }
   455 
   456 /*
   457  * Note that undo has no error checking.
   458  */
   459 
   460 void atomic_action_undo(struct razor_atomic *atomic,
   461 			struct atomic_action *actions)
   462 {
   463 	struct atomic_action *a;
   464 
   465 	atomic->in_undo = 1;
   466 
   467 	while (actions) {
   468 		a = atomic_action_list_pop_head(&actions);
   469 		a = atomic_action_reverse(atomic, a);
   470 		atomic_action_free(a);
   471 	}
   472 
   473 	atomic->in_undo = 0;
   474 }
   475 
   476 struct atomic_action *atomic_action_do(struct razor_atomic *atomic,
   477 				       struct atomic_action *actions)
   478 {
   479 	struct atomic_action *done = NULL, *a;
   480 
   481 	if (razor_atomic_in_error_state(atomic)) {
   482 		atomic_action_free(actions);
   483 		return NULL;
   484 	}
   485 
   486 	while (actions) {
   487 		a = atomic_action_list_pop_head(&actions);
   488 		a = atomic_action_apply(atomic, a);
   489 		if (a)
   490 			done = atomic_action_list_concat(a, done);
   491 		if (razor_atomic_in_error_state(atomic)) {
   492 			atomic_action_undo(atomic, done);
   493 			done = NULL;
   494 			atomic_action_free(a);
   495 		}
   496 	}
   497 
   498 	return done;
   499 }
   500 
   501 #endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */