librazor/atomic-actions.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Oct 04 18:12:58 2014 +0100 (2014-10-04)
changeset 454 56ff755c268c
parent 447 0a5e583393e1
child 458 3f841a46eab5
permissions -rw-r--r--
Only export symbols starting with razor_ in dynamic library.

Apart from being good practice to avoid clashes with higher-level
libraries and the application, this also fixes an obscure bug: The
gnulib library is used both by librazor (the dynamic library) and
by razor (the executable). In doing so, we want to have two separate
copies of the library despite the code duplication this involves.
Without the explicit limit to export only razor_ symbols, the razor
executable under mingw64 was picking up the getopt_long function
from librazor and the optind variable from libgnu which meant that
it did not see optind changing. Hiding librazor's copy of getopt
causes the linker to find libgnu's copy and everything works.

Note that under mingw librazor-#.dll still contains undocumented
(private) razor_ symbols but these will do no harm as long as nobody
tries to use them.
     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 */