librazor/atomic-actions.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Feb 09 20:45:27 2012 +0000 (2012-02-09)
changeset 418 33b825d3128d
child 423 6112bcc5d1cf
permissions -rw-r--r--
Add transaction barriers
These allow packages to be installed and removed which have scripts
that depend on each other when atomic transactions are involved.
Note that yum supports pre, but not other requires flags. post will
need similar support to the post scripts themselves pulling in the
requires flags from the rpms. Likewise preun and postun will need
similar handling to those scrips since the requires flags will need
to be stored in the razor database.
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@416
   250
ali@416
   251
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   252
		atomic_action_free(action);
ali@416
   253
		return NULL;
ali@416
   254
	}
ali@416
   255
ali@416
   256
	mode = action->args.u.create_dir.mode & (S_IRWXU | S_IRWXG | S_IRWXO);
ali@416
   257
ali@416
   258
	if (!mkdir(action->args.path, mode))
ali@416
   259
		return 0;
ali@416
   260
ali@416
   261
	if (errno != EEXIST || chmod(action->args.path, mode) < 0) {
ali@416
   262
		if (!atomic->error_str)
ali@416
   263
			razor_atomic_set_error_str(atomic, action->args.path,
ali@416
   264
						   strerror(errno));
ali@416
   265
		atomic_action_free(action);
ali@416
   266
		return NULL;
ali@416
   267
	}
ali@416
   268
ali@416
   269
	return action;
ali@416
   270
}
ali@416
   271
ali@416
   272
static struct atomic_action *atomic_action_rmdir(struct razor_atomic *atomic,
ali@416
   273
						 struct atomic_action *action)
ali@416
   274
{
ali@416
   275
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   276
		atomic_action_free(action);
ali@416
   277
		return NULL;
ali@416
   278
	}
ali@416
   279
ali@416
   280
	if (rmdir(action->args.path) < 0) {
ali@416
   281
		if (!atomic->error_str)
ali@416
   282
			razor_atomic_set_error_str(atomic, action->args.path,
ali@416
   283
						   strerror(errno));
ali@416
   284
		atomic_action_free(action);
ali@416
   285
		return NULL;
ali@416
   286
	} else
ali@416
   287
		return action;
ali@416
   288
}
ali@416
   289
ali@416
   290
#if HAVE_SYMLINK
ali@416
   291
static struct atomic_action *
ali@416
   292
atomic_action_create_symlink(struct razor_atomic *atomic,
ali@416
   293
			     struct atomic_action *action)
ali@416
   294
{
ali@416
   295
	int r;
ali@416
   296
ali@416
   297
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   298
		atomic_action_free(action);
ali@416
   299
		return NULL;
ali@416
   300
	}
ali@416
   301
ali@416
   302
	r = symlink(action->args.u.create_symlink.target, action->args.path);
ali@416
   303
	if (r < 0) {
ali@416
   304
		if (!atomic->error_str)
ali@416
   305
			razor_atomic_set_error_str(atomic, NULL,
ali@416
   306
						   strerror(errno));
ali@416
   307
		atomic_action_free(action);
ali@416
   308
		return NULL;
ali@416
   309
	}
ali@416
   310
ali@416
   311
	return action;
ali@416
   312
}
ali@416
   313
ali@416
   314
static struct atomic_action *
ali@416
   315
atomic_action_remove_symlink(struct razor_atomic *atomic,
ali@416
   316
			     struct atomic_action *action)
ali@416
   317
{
ali@416
   318
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   319
		atomic_action_free(action);
ali@416
   320
		return NULL;
ali@416
   321
	}
ali@416
   322
ali@416
   323
	if (unlink(action->args.path) < 0) {
ali@416
   324
		if (!atomic->error_str)
ali@416
   325
			razor_atomic_set_error_str(atomic, NULL,
ali@416
   326
						   strerror(errno));
ali@416
   327
		atomic_action_free(action);
ali@416
   328
		return NULL;
ali@416
   329
	}
ali@416
   330
ali@416
   331
	return action;
ali@416
   332
}
ali@416
   333
#endif
ali@416
   334
ali@416
   335
static int
ali@416
   336
move_file(struct razor_atomic *atomic, const char *path, const char *dest)
ali@416
   337
{
ali@416
   338
#ifdef MSWIN_API
ali@416
   339
	wchar_t *oldbuf, *newbuf;
ali@416
   340
	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
ali@416
   341
ali@416
   342
	newbuf = razor_utf8_to_utf16(dest, -1);
ali@416
   343
	oldbuf = razor_utf8_to_utf16(path, -1);
ali@416
   344
ali@416
   345
	/*
ali@416
   346
	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
ali@416
   347
	 * cover every case we care about _except_ replacing an empty
ali@416
   348
	 * directory with a file. Calling RemoveDirectory() will deal
ali@416
   349
	 * with this case while having no effect in all other cases.
ali@416
   350
	 */
ali@416
   351
	(void)RemoveDirectoryW(newbuf);
ali@416
   352
ali@416
   353
	if (!MoveFileExW(oldbuf, newbuf, flags)) {
ali@416
   354
		if (!atomic->error_str)
ali@416
   355
			razor_atomic_set_error_mswin(atomic, newbuf,
ali@416
   356
						     GetLastError());
ali@416
   357
		return -1;
ali@416
   358
	}
ali@416
   359
ali@416
   360
	free(newbuf);
ali@416
   361
	free(oldbuf);
ali@416
   362
#else
ali@416
   363
	if (rename(path, dest)) {
ali@416
   364
		if (!atomic->error_str)
ali@416
   365
			razor_atomic_set_error_str(atomic, dest,
ali@416
   366
						   strerror(errno));
ali@416
   367
		return -1;
ali@416
   368
	}
ali@416
   369
#endif
ali@416
   370
ali@416
   371
	return 0;
ali@416
   372
}
ali@416
   373
ali@416
   374
static struct atomic_action *
ali@416
   375
atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action)
ali@416
   376
{
ali@416
   377
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   378
		atomic_action_free(action);
ali@416
   379
		return NULL;
ali@416
   380
	}
ali@416
   381
ali@416
   382
	if (move_file(atomic, action->args.path, action->args.u.move.dest)) {
ali@416
   383
		atomic_action_free(action);
ali@416
   384
		return NULL;
ali@416
   385
	}
ali@416
   386
ali@416
   387
	return action;
ali@416
   388
}
ali@416
   389
ali@416
   390
static struct atomic_action *
ali@416
   391
atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action)
ali@416
   392
{
ali@416
   393
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   394
		atomic_action_free(action);
ali@416
   395
		return NULL;
ali@416
   396
	}
ali@416
   397
ali@416
   398
	if (move_file(atomic, action->args.u.move.dest, action->args.path)) {
ali@416
   399
		atomic_action_free(action);
ali@416
   400
		return NULL;
ali@416
   401
	}
ali@416
   402
ali@416
   403
	return action;
ali@416
   404
}
ali@416
   405
ali@416
   406
static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic,
ali@416
   407
						 struct atomic_action *action)
ali@416
   408
{
ali@416
   409
	switch(action->type) {
ali@416
   410
	case ACTION_MAKE_DIRS:
ali@416
   411
		action = atomic_action_make_dirs(atomic, action);
ali@416
   412
		break;
ali@416
   413
	case ACTION_REMOVE:
ali@416
   414
		action = atomic_action_remove(atomic, action);
ali@416
   415
		break;
ali@416
   416
	case ACTION_CREATE_DIR:
ali@416
   417
		action = atomic_action_create_dir(atomic, action);
ali@416
   418
		break;
ali@416
   419
	case ACTION_MOVE:
ali@416
   420
		action = atomic_action_move(atomic, action);
ali@416
   421
		break;
ali@416
   422
#if HAVE_SYMLINK
ali@416
   423
	case ACTION_CREATE_SYMLINK:
ali@416
   424
		action = atomic_action_create_symlink(atomic, action);
ali@416
   425
		break;
ali@416
   426
#endif
ali@416
   427
	}
ali@416
   428
	return action;
ali@416
   429
}
ali@416
   430
ali@416
   431
static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic,
ali@416
   432
						   struct atomic_action *action)
ali@416
   433
{
ali@416
   434
	switch(action->type) {
ali@416
   435
	case ACTION_MAKE_DIRS:
ali@416
   436
	case ACTION_REMOVE:
ali@416
   437
		/* Complex actions: should never happen */
ali@416
   438
		break;
ali@416
   439
	case ACTION_CREATE_DIR:
ali@416
   440
		action = atomic_action_rmdir(atomic, action);
ali@416
   441
		break;
ali@416
   442
	case ACTION_MOVE:
ali@416
   443
		action = atomic_action_unmove(atomic, action);
ali@416
   444
		break;
ali@416
   445
#if HAVE_SYMLINK
ali@416
   446
	case ACTION_CREATE_SYMLINK:
ali@416
   447
		action = atomic_action_remove_symlink(atomic, action);
ali@416
   448
		break;
ali@416
   449
#endif
ali@416
   450
	}
ali@416
   451
	return action;
ali@416
   452
}
ali@416
   453
ali@416
   454
/*
ali@416
   455
 * Note that undo has no error checking.
ali@416
   456
 */
ali@416
   457
ali@416
   458
void atomic_action_undo(struct razor_atomic *atomic,
ali@416
   459
			struct atomic_action *actions)
ali@416
   460
{
ali@416
   461
	struct atomic_action *a;
ali@416
   462
ali@416
   463
	atomic->in_undo = 1;
ali@416
   464
ali@416
   465
	while (actions) {
ali@416
   466
		a = atomic_action_list_pop_head(&actions);
ali@416
   467
		a = atomic_action_reverse(atomic, a);
ali@416
   468
		atomic_action_free(a);
ali@416
   469
	}
ali@416
   470
ali@416
   471
	atomic->in_undo = 0;
ali@416
   472
}
ali@416
   473
ali@416
   474
struct atomic_action *atomic_action_do(struct razor_atomic *atomic,
ali@416
   475
				       struct atomic_action *actions)
ali@416
   476
{
ali@416
   477
	struct atomic_action *done = NULL, *a;
ali@416
   478
ali@416
   479
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   480
		atomic_action_free(actions);
ali@416
   481
		return NULL;
ali@416
   482
	}
ali@416
   483
ali@416
   484
	while (actions) {
ali@416
   485
		a = atomic_action_list_pop_head(&actions);
ali@416
   486
		a = atomic_action_apply(atomic, a);
ali@416
   487
		if (a)
ali@416
   488
			done = atomic_action_list_concat(a, done);
ali@416
   489
		if (razor_atomic_in_error_state(atomic)) {
ali@416
   490
			atomic_action_undo(atomic, done);
ali@416
   491
			done = NULL;
ali@416
   492
			atomic_action_free(actions);
ali@416
   493
		}
ali@416
   494
	}
ali@416
   495
ali@416
   496
	return done;
ali@416
   497
}
ali@416
   498
ali@416
   499
#endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */