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