librazor/atomic-actions.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Nov 13 10:44:53 2014 +0000 (2014-11-13)
changeset 462 94d7459828ba
parent 450 f969505e9265
child 471 a3e5e3eaf224
permissions -rw-r--r--
Add razor_install_prefix_iterator_create()
     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 	atomic_action_free(action);
   194 
   195 	return atomic_action_do(atomic, primitives);
   196 }
   197 
   198 static struct atomic_action *
   199 atomic_action_remove(struct razor_atomic *atomic, struct atomic_action *action)
   200 {
   201 #ifdef MSWIN_API
   202 	wchar_t *path;
   203 	_WDIR *dir;
   204 #else
   205 	DIR *dir;
   206 #endif
   207 	struct atomic_action *prim;
   208 
   209 	if (razor_atomic_in_error_state(atomic)) {
   210 		atomic_action_free(action);
   211 		return NULL;
   212 	}
   213 
   214 	/*
   215 	 * Non-empty directories should NOT be removed
   216 	 */
   217 #ifdef MSWIN_API
   218 	path = razor_utf8_to_utf16(action->args.path, -1);
   219 
   220 	dir = _wopendir(path);
   221 	if (dir && _wreaddir(dir)) {
   222 		atomic_action_free(action);
   223 		action = NULL;
   224 	}
   225 	_wclosedir(dir);
   226 #else
   227 	dir = opendir(action->args.path);
   228 	if (dir && readdir(dir)) {
   229 		atomic_action_free(action);
   230 		action = NULL;
   231 	}
   232 	closedir(dir);
   233 #endif
   234 
   235 	if (action) {
   236 		prim = atomic_action_new(ACTION_MOVE);
   237 		prim->args.path = strdup(action->args.path);
   238 		prim->args.u.move.dest = atomic_action_attic_tmpnam(atomic);
   239 
   240 		atomic_action_free(action);
   241 	} else
   242 		prim = NULL;
   243 
   244 	return prim ? atomic_action_do(atomic, prim) : NULL;
   245 }
   246 
   247 static struct atomic_action *
   248 atomic_action_create_dir(struct razor_atomic *atomic,
   249 			 struct atomic_action *action)
   250 {
   251 	mode_t mode;
   252 	struct stat buf;
   253 
   254 	if (razor_atomic_in_error_state(atomic)) {
   255 		atomic_action_free(action);
   256 		return NULL;
   257 	}
   258 
   259 	mode = action->args.u.create_dir.mode & (S_IRWXU | S_IRWXG | S_IRWXO);
   260 
   261 	if (!mkdir(action->args.path, mode))
   262 		return action;
   263 
   264 	if (errno != EEXIST || stat(action->args.path, &buf)) {
   265 		if (!atomic->error)
   266 			atomic->error = razor_error_new_posix(action->args.path);
   267 		atomic_action_free(action);
   268 		return NULL;
   269 	}
   270 
   271 	if (!S_ISDIR(buf.st_mode) && !atomic->error)
   272 		atomic->error = razor_error_new_str(RAZOR_POSIX_ERROR, EEXIST,
   273 						    action->args.path,
   274 						    "Not a directory");
   275 
   276 	atomic_action_free(action);
   277 	return NULL;
   278 }
   279 
   280 static struct atomic_action *atomic_action_rmdir(struct razor_atomic *atomic,
   281 						 struct atomic_action *action)
   282 {
   283 	if (razor_atomic_in_error_state(atomic)) {
   284 		atomic_action_free(action);
   285 		return NULL;
   286 	}
   287 
   288 	if (rmdir(action->args.path) < 0) {
   289 		if (!atomic->error)
   290 			atomic->error = razor_error_new_posix(action->args.path);
   291 		atomic_action_free(action);
   292 		return NULL;
   293 	} else
   294 		return action;
   295 }
   296 
   297 #if HAVE_SYMLINK
   298 static struct atomic_action *
   299 atomic_action_create_symlink(struct razor_atomic *atomic,
   300 			     struct atomic_action *action)
   301 {
   302 	int r;
   303 
   304 	if (razor_atomic_in_error_state(atomic)) {
   305 		atomic_action_free(action);
   306 		return NULL;
   307 	}
   308 
   309 	r = symlink(action->args.u.create_symlink.target, action->args.path);
   310 	if (r < 0) {
   311 		if (!atomic->error)
   312 			atomic->error = razor_error_new_posix(action->args.path);
   313 		atomic_action_free(action);
   314 		return NULL;
   315 	}
   316 
   317 	return action;
   318 }
   319 
   320 static struct atomic_action *
   321 atomic_action_remove_symlink(struct razor_atomic *atomic,
   322 			     struct atomic_action *action)
   323 {
   324 	if (razor_atomic_in_error_state(atomic)) {
   325 		atomic_action_free(action);
   326 		return NULL;
   327 	}
   328 
   329 	if (unlink(action->args.path) < 0) {
   330 		if (!atomic->error)
   331 			atomic->error = razor_error_new_posix(action->args.path);
   332 		atomic_action_free(action);
   333 		return NULL;
   334 	}
   335 
   336 	return action;
   337 }
   338 #endif
   339 
   340 static int
   341 move_file(struct razor_atomic *atomic, const char *path, const char *dest)
   342 {
   343 #ifdef MSWIN_API
   344 	wchar_t *oldbuf, *newbuf;
   345 	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
   346 
   347 	newbuf = razor_utf8_to_utf16(dest, -1);
   348 	oldbuf = razor_utf8_to_utf16(path, -1);
   349 
   350 	/*
   351 	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
   352 	 * cover every case we care about _except_ replacing an empty
   353 	 * directory with a file. Calling RemoveDirectory() will deal
   354 	 * with this case while having no effect in all other cases.
   355 	 */
   356 	(void)RemoveDirectoryW(newbuf);
   357 
   358 	if (!MoveFileExW(oldbuf, newbuf, flags)) {
   359 		if (!atomic->error)
   360 			atomic->error = razor_error_new_mswin(newbuf,
   361 							      GetLastError());
   362 		return -1;
   363 	}
   364 
   365 	free(newbuf);
   366 	free(oldbuf);
   367 #else
   368 	if (rename(path, dest)) {
   369 		if (!atomic->error)
   370 			atomic->error = razor_error_new_posix(dest);
   371 		return -1;
   372 	}
   373 #endif
   374 
   375 	return 0;
   376 }
   377 
   378 static struct atomic_action *
   379 atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action)
   380 {
   381 	if (razor_atomic_in_error_state(atomic)) {
   382 		atomic_action_free(action);
   383 		return NULL;
   384 	}
   385 
   386 	if (move_file(atomic, action->args.path, action->args.u.move.dest)) {
   387 		atomic_action_free(action);
   388 		return NULL;
   389 	}
   390 
   391 	return action;
   392 }
   393 
   394 static struct atomic_action *
   395 atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action)
   396 {
   397 	if (razor_atomic_in_error_state(atomic)) {
   398 		atomic_action_free(action);
   399 		return NULL;
   400 	}
   401 
   402 	if (move_file(atomic, action->args.u.move.dest, action->args.path)) {
   403 		atomic_action_free(action);
   404 		return NULL;
   405 	}
   406 
   407 	return action;
   408 }
   409 
   410 static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic,
   411 						 struct atomic_action *action)
   412 {
   413 	switch(action->type) {
   414 	case ACTION_MAKE_DIRS:
   415 		action = atomic_action_make_dirs(atomic, action);
   416 		break;
   417 	case ACTION_REMOVE:
   418 		action = atomic_action_remove(atomic, action);
   419 		break;
   420 	case ACTION_CREATE_DIR:
   421 		action = atomic_action_create_dir(atomic, action);
   422 		break;
   423 	case ACTION_MOVE:
   424 		action = atomic_action_move(atomic, action);
   425 		break;
   426 #if HAVE_SYMLINK
   427 	case ACTION_CREATE_SYMLINK:
   428 		action = atomic_action_create_symlink(atomic, action);
   429 		break;
   430 #endif
   431 	}
   432 	return action;
   433 }
   434 
   435 static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic,
   436 						   struct atomic_action *action)
   437 {
   438 	switch(action->type) {
   439 	case ACTION_MAKE_DIRS:
   440 	case ACTION_REMOVE:
   441 		/* Complex actions: should never happen */
   442 		break;
   443 	case ACTION_CREATE_DIR:
   444 		action = atomic_action_rmdir(atomic, action);
   445 		break;
   446 	case ACTION_MOVE:
   447 		action = atomic_action_unmove(atomic, action);
   448 		break;
   449 #if HAVE_SYMLINK
   450 	case ACTION_CREATE_SYMLINK:
   451 		action = atomic_action_remove_symlink(atomic, action);
   452 		break;
   453 #endif
   454 	}
   455 	return action;
   456 }
   457 
   458 /*
   459  * Note that undo has no error checking.
   460  */
   461 
   462 void atomic_action_undo(struct razor_atomic *atomic,
   463 			struct atomic_action *actions)
   464 {
   465 	struct atomic_action *a;
   466 
   467 	atomic->in_undo = 1;
   468 
   469 	while (actions) {
   470 		a = atomic_action_list_pop_head(&actions);
   471 		a = atomic_action_reverse(atomic, a);
   472 		atomic_action_free(a);
   473 	}
   474 
   475 	atomic->in_undo = 0;
   476 }
   477 
   478 struct atomic_action *atomic_action_do(struct razor_atomic *atomic,
   479 				       struct atomic_action *actions)
   480 {
   481 	struct atomic_action *done = NULL, *a;
   482 
   483 	if (razor_atomic_in_error_state(atomic)) {
   484 		atomic_action_free(actions);
   485 		return NULL;
   486 	}
   487 
   488 	while (actions) {
   489 		a = atomic_action_list_pop_head(&actions);
   490 		a = atomic_action_apply(atomic, a);
   491 		if (a)
   492 			done = atomic_action_list_concat(a, done);
   493 		if (razor_atomic_in_error_state(atomic)) {
   494 			atomic_action_undo(atomic, done);
   495 			done = NULL;
   496 			atomic_action_free(a);
   497 		}
   498 	}
   499 
   500 	return done;
   501 }
   502 
   503 #endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */