librazor/atomic-actions.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Jun 11 17:56:48 2016 +0100 (2016-06-11)
changeset 474 7442b30ecaae
parent 458 3f841a46eab5
child 475 008c75a5e08d
permissions -rw-r--r--
Added tag 0.6.3 for changeset 2e8a0b26d579
     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 	int code;
   369 	const char *object;
   370 
   371 	if (rename(path, dest)) {
   372 		if (!atomic->error) {
   373 			code = errno;
   374 			if (access(path, F_OK) < 0)
   375 				object = path;
   376 			else
   377 				object = dest;
   378 			atomic->error = razor_error_new_str(RAZOR_POSIX_ERROR,
   379 							    code, object,
   380 							    strerror(code));
   381 		}
   382 		return -1;
   383 	}
   384 #endif
   385 
   386 	return 0;
   387 }
   388 
   389 static struct atomic_action *
   390 atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action)
   391 {
   392 	if (razor_atomic_in_error_state(atomic)) {
   393 		atomic_action_free(action);
   394 		return NULL;
   395 	}
   396 
   397 	if (move_file(atomic, action->args.path, action->args.u.move.dest)) {
   398 		atomic_action_free(action);
   399 		return NULL;
   400 	}
   401 
   402 	return action;
   403 }
   404 
   405 static struct atomic_action *
   406 atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action)
   407 {
   408 	if (razor_atomic_in_error_state(atomic)) {
   409 		atomic_action_free(action);
   410 		return NULL;
   411 	}
   412 
   413 	if (move_file(atomic, action->args.u.move.dest, action->args.path)) {
   414 		atomic_action_free(action);
   415 		return NULL;
   416 	}
   417 
   418 	return action;
   419 }
   420 
   421 static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic,
   422 						 struct atomic_action *action)
   423 {
   424 	switch(action->type) {
   425 	case ACTION_MAKE_DIRS:
   426 		action = atomic_action_make_dirs(atomic, action);
   427 		break;
   428 	case ACTION_REMOVE:
   429 		action = atomic_action_remove(atomic, action);
   430 		break;
   431 	case ACTION_CREATE_DIR:
   432 		action = atomic_action_create_dir(atomic, action);
   433 		break;
   434 	case ACTION_MOVE:
   435 		action = atomic_action_move(atomic, action);
   436 		break;
   437 #if HAVE_SYMLINK
   438 	case ACTION_CREATE_SYMLINK:
   439 		action = atomic_action_create_symlink(atomic, action);
   440 		break;
   441 #endif
   442 	}
   443 	return action;
   444 }
   445 
   446 static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic,
   447 						   struct atomic_action *action)
   448 {
   449 	switch(action->type) {
   450 	case ACTION_MAKE_DIRS:
   451 	case ACTION_REMOVE:
   452 		/* Complex actions: should never happen */
   453 		break;
   454 	case ACTION_CREATE_DIR:
   455 		action = atomic_action_rmdir(atomic, action);
   456 		break;
   457 	case ACTION_MOVE:
   458 		action = atomic_action_unmove(atomic, action);
   459 		break;
   460 #if HAVE_SYMLINK
   461 	case ACTION_CREATE_SYMLINK:
   462 		action = atomic_action_remove_symlink(atomic, action);
   463 		break;
   464 #endif
   465 	}
   466 	return action;
   467 }
   468 
   469 /*
   470  * Note that undo has no error checking.
   471  */
   472 
   473 void atomic_action_undo(struct razor_atomic *atomic,
   474 			struct atomic_action *actions)
   475 {
   476 	struct atomic_action *a;
   477 
   478 	atomic->in_undo = 1;
   479 
   480 	while (actions) {
   481 		a = atomic_action_list_pop_head(&actions);
   482 		a = atomic_action_reverse(atomic, a);
   483 		atomic_action_free(a);
   484 	}
   485 
   486 	atomic->in_undo = 0;
   487 }
   488 
   489 struct atomic_action *atomic_action_do(struct razor_atomic *atomic,
   490 				       struct atomic_action *actions)
   491 {
   492 	struct atomic_action *done = NULL, *a;
   493 
   494 	if (razor_atomic_in_error_state(atomic)) {
   495 		atomic_action_free(actions);
   496 		return NULL;
   497 	}
   498 
   499 	while (actions) {
   500 		a = atomic_action_list_pop_head(&actions);
   501 		a = atomic_action_apply(atomic, a);
   502 		if (a)
   503 			done = atomic_action_list_concat(a, done);
   504 		if (razor_atomic_in_error_state(atomic)) {
   505 			atomic_action_undo(atomic, done);
   506 			done = NULL;
   507 			atomic_action_free(a);
   508 		}
   509 	}
   510 
   511 	return done;
   512 }
   513 
   514 #endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */