librazor/atomic-actions.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Jul 08 15:54:09 2016 +0100 (2016-07-08)
changeset 481 c3722fff46c7
parent 471 a3e5e3eaf224
permissions -rw-r--r--
Windows tweaks for archives
ali@416
     1
/*
ali@475
     2
 * Copyright (C) 2012, 2016  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 <assert.h>
ali@416
    29
#include "razor-internal.h"
ali@416
    30
ali@416
    31
char *atomic_action_attic_tmpnam(struct razor_atomic *atomic)
ali@416
    32
{
ali@416
    33
	char filename[17];
ali@416
    34
	sprintf(filename,"%03X",atomic->next_file_tag++);
ali@416
    35
	return razor_concat(atomic->toplevel, "/", filename, NULL);
ali@416
    36
}
ali@416
    37
ali@416
    38
static struct atomic_action *
ali@416
    39
atomic_action_list_pop_head(struct atomic_action **list)
ali@416
    40
{
ali@416
    41
	struct atomic_action *head;
ali@416
    42
ali@416
    43
	head = *list;
ali@416
    44
	if (head) {
ali@416
    45
		*list = head->next;
ali@416
    46
		head->next = NULL;
ali@416
    47
	}
ali@416
    48
ali@416
    49
	return head;
ali@416
    50
}
ali@416
    51
ali@416
    52
struct atomic_action *
ali@416
    53
atomic_action_list_prepend(struct atomic_action *list,
ali@416
    54
			   struct atomic_action *action)
ali@416
    55
{
ali@416
    56
	assert(action->next == NULL);
ali@416
    57
ali@416
    58
	action->next=list;
ali@416
    59
ali@416
    60
	return action;
ali@416
    61
}
ali@416
    62
ali@416
    63
static struct atomic_action *
ali@416
    64
atomic_action_list_concat(struct atomic_action *list1,
ali@416
    65
			  struct atomic_action *list2)
ali@416
    66
{
ali@416
    67
	struct atomic_action *action;
ali@416
    68
ali@416
    69
	if (!list1)
ali@416
    70
		return list2;
ali@416
    71
ali@416
    72
	for(action=list1;action->next;action=action->next)
ali@416
    73
		;
ali@416
    74
ali@416
    75
	action->next=list2;
ali@416
    76
ali@416
    77
	return list1;
ali@416
    78
}
ali@416
    79
ali@416
    80
void atomic_action_free(struct atomic_action *action)
ali@416
    81
{
ali@416
    82
	struct atomic_action *a;
ali@416
    83
ali@416
    84
	while(action) {
ali@416
    85
		a = atomic_action_list_pop_head(&action);
ali@416
    86
ali@475
    87
		free(a->args.uri);
ali@416
    88
ali@416
    89
		switch(a->type) {
ali@416
    90
		case ACTION_MAKE_DIRS:
ali@416
    91
			free(a->args.u.make_dirs.root);
ali@416
    92
			break;
ali@416
    93
		case ACTION_MOVE:
ali@416
    94
			free(a->args.u.move.dest);
ali@416
    95
			break;
ali@416
    96
#if HAVE_SYMLINK
ali@416
    97
		case ACTION_CREATE_SYMLINK:
ali@416
    98
			free(a->args.u.create_symlink.target);
ali@416
    99
			break;
ali@416
   100
#endif
ali@416
   101
		case ACTION_REMOVE:
ali@416
   102
		case ACTION_CREATE_DIR:
ali@416
   103
			break;
ali@416
   104
		}
ali@416
   105
ali@416
   106
		free(a);
ali@416
   107
	}
ali@416
   108
}
ali@416
   109
ali@416
   110
struct atomic_action *atomic_action_new(enum atomic_action_type type)
ali@416
   111
{
ali@416
   112
	struct atomic_action *action;
ali@416
   113
ali@416
   114
	action = zalloc(sizeof *action);
ali@416
   115
	action->type = type;
ali@416
   116
ali@416
   117
	return action;
ali@416
   118
}
ali@416
   119
ali@416
   120
struct atomic_action *atomic_action_list_reverse(struct atomic_action *list)
ali@416
   121
{
ali@416
   122
	struct atomic_action *prev = NULL, *next;
ali@416
   123
ali@416
   124
	while(list) {
ali@416
   125
		next = list->next;
ali@416
   126
		list->next = prev;
ali@416
   127
		prev = list;
ali@416
   128
		list = next;
ali@416
   129
	}
ali@416
   130
ali@416
   131
	return prev;
ali@416
   132
}
ali@416
   133
ali@416
   134
/*
ali@416
   135
 * All action_ functions take 1 action and return a list of primitive
ali@416
   136
 * actions they took (such that the first action in the list is the
ali@416
   137
 * last action they took). Primitive actions are always reversable.
ali@416
   138
 *
ali@416
   139
 * On failure, an error should be set on the atomic object (if it is
ali@416
   140
 * not already set), the action freed and NULL returned.
ali@416
   141
 *
ali@416
   142
 * Whether they succeed or fail, they take ownership of the passed
ali@416
   143
 * action and should thus either free it or return it back to their
ali@416
   144
 * caller as appropriate.
ali@416
   145
 *
ali@416
   146
 * A NULL return means that nothing was done which may, or may not,
ali@416
   147
 * indicate an error.
ali@416
   148
 */
ali@416
   149
ali@416
   150
static struct atomic_action *
ali@416
   151
atomic_action_make_dirs(struct razor_atomic *atomic,
ali@416
   152
			struct atomic_action *action)
ali@416
   153
{
ali@416
   154
	char buffer[PATH_MAX], *p;
ali@416
   155
	const char *slash, *next;
ali@416
   156
	struct atomic_action *primitives = NULL;
ali@416
   157
	struct atomic_action *prim;
ali@416
   158
ali@416
   159
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   160
		atomic_action_free(action);
ali@416
   161
		return NULL;
ali@416
   162
	}
ali@416
   163
ali@416
   164
	strcpy(buffer, action->args.u.make_dirs.root);
ali@416
   165
	p = buffer + strlen(buffer);
ali@475
   166
	slash = action->args.uri;
ali@475
   167
	if (p > buffer && p[-1] != ':' && p[-1] != '/' && *slash != '/')
ali@475
   168
		*p++ = '/';
ali@416
   169
	for (; *slash != '\0'; slash = next) {
ali@416
   170
		next = strchr(slash + 1, '/');
ali@416
   171
		if (next == NULL)
ali@416
   172
			break;
ali@416
   173
ali@416
   174
		memcpy(p, slash, next - slash);
ali@416
   175
		p += next - slash;
ali@416
   176
		*p = '\0';
ali@416
   177
ali@416
   178
		if (razor_valid_root_name(buffer))
ali@416
   179
			continue;
ali@416
   180
ali@416
   181
		prim = atomic_action_new(ACTION_CREATE_DIR);
ali@475
   182
		prim->args.uri = strdup(buffer);
ali@416
   183
		prim->args.u.create_dir.mode = S_IRWXU | S_IRWXG | S_IRWXO;
ali@416
   184
		primitives = atomic_action_list_prepend(primitives, prim);
ali@416
   185
	}
ali@416
   186
	primitives = atomic_action_list_reverse(primitives);
ali@416
   187
ali@458
   188
	atomic_action_free(action);
ali@458
   189
ali@416
   190
	return atomic_action_do(atomic, primitives);
ali@416
   191
}
ali@416
   192
ali@416
   193
static struct atomic_action *
ali@416
   194
atomic_action_remove(struct razor_atomic *atomic, struct atomic_action *action)
ali@416
   195
{
ali@475
   196
	void *dir;
ali@475
   197
	char *name;
ali@416
   198
	struct atomic_action *prim;
ali@416
   199
ali@416
   200
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   201
		atomic_action_free(action);
ali@416
   202
		return NULL;
ali@416
   203
	}
ali@416
   204
ali@416
   205
	/*
ali@416
   206
	 * Non-empty directories should NOT be removed
ali@416
   207
	 */
ali@475
   208
	dir = razor_uri_opendir(action->args.uri, NULL);
ali@475
   209
	if (dir) {
ali@475
   210
		name = razor_uri_readdir(dir, NULL);
ali@475
   211
		razor_uri_closedir(dir, NULL);
ali@475
   212
		if (name) {
ali@475
   213
			free(name);
ali@475
   214
			atomic_action_free(action);
ali@475
   215
			action = NULL;
ali@475
   216
		}
ali@416
   217
	}
ali@416
   218
ali@416
   219
	if (action) {
ali@416
   220
		prim = atomic_action_new(ACTION_MOVE);
ali@475
   221
		prim->args.uri = strdup(action->args.uri);
ali@416
   222
		prim->args.u.move.dest = atomic_action_attic_tmpnam(atomic);
ali@416
   223
ali@416
   224
		atomic_action_free(action);
ali@416
   225
	} else
ali@416
   226
		prim = NULL;
ali@416
   227
ali@416
   228
	return prim ? atomic_action_do(atomic, prim) : NULL;
ali@416
   229
}
ali@416
   230
ali@416
   231
static struct atomic_action *
ali@416
   232
atomic_action_create_dir(struct razor_atomic *atomic,
ali@416
   233
			 struct atomic_action *action)
ali@416
   234
{
ali@416
   235
	mode_t mode;
ali@475
   236
	struct razor_error **error;
ali@416
   237
ali@416
   238
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   239
		atomic_action_free(action);
ali@416
   240
		return NULL;
ali@416
   241
	}
ali@416
   242
ali@416
   243
	mode = action->args.u.create_dir.mode & (S_IRWXU | S_IRWXG | S_IRWXO);
ali@416
   244
ali@475
   245
	if (atomic->error)
ali@475
   246
		error = NULL;
ali@475
   247
	else
ali@475
   248
		error = &atomic->error;
ali@475
   249
ali@475
   250
	if (!razor_uri_mkdir(action->args.uri, mode, error))
ali@450
   251
		return action;
ali@416
   252
ali@450
   253
	atomic_action_free(action);
ali@450
   254
	return NULL;
ali@416
   255
}
ali@416
   256
ali@416
   257
static struct atomic_action *atomic_action_rmdir(struct razor_atomic *atomic,
ali@416
   258
						 struct atomic_action *action)
ali@416
   259
{
ali@416
   260
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   261
		atomic_action_free(action);
ali@416
   262
		return NULL;
ali@416
   263
	}
ali@416
   264
ali@475
   265
	if (rmdir(action->args.uri) < 0) {
ali@423
   266
		if (!atomic->error)
ali@475
   267
			atomic->error = razor_error_new_posix(action->args.uri);
ali@416
   268
		atomic_action_free(action);
ali@416
   269
		return NULL;
ali@416
   270
	} else
ali@416
   271
		return action;
ali@416
   272
}
ali@416
   273
ali@416
   274
#if HAVE_SYMLINK
ali@416
   275
static struct atomic_action *
ali@416
   276
atomic_action_create_symlink(struct razor_atomic *atomic,
ali@416
   277
			     struct atomic_action *action)
ali@416
   278
{
ali@416
   279
	int r;
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@475
   286
	r = symlink(action->args.u.create_symlink.target, action->args.uri);
ali@416
   287
	if (r < 0) {
ali@423
   288
		if (!atomic->error)
ali@475
   289
			atomic->error = razor_error_new_posix(action->args.uri);
ali@416
   290
		atomic_action_free(action);
ali@416
   291
		return NULL;
ali@416
   292
	}
ali@416
   293
ali@416
   294
	return action;
ali@416
   295
}
ali@416
   296
ali@416
   297
static struct atomic_action *
ali@416
   298
atomic_action_remove_symlink(struct razor_atomic *atomic,
ali@416
   299
			     struct atomic_action *action)
ali@416
   300
{
ali@475
   301
	struct razor_error **error;
ali@475
   302
ali@416
   303
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   304
		atomic_action_free(action);
ali@416
   305
		return NULL;
ali@416
   306
	}
ali@416
   307
ali@475
   308
	if (atomic->error)
ali@475
   309
		error = NULL;
ali@475
   310
	else
ali@475
   311
		error = &atomic->error;
ali@475
   312
ali@475
   313
	if (razor_uri_unlink(action->args.uri, error)) {
ali@416
   314
		atomic_action_free(action);
ali@416
   315
		return NULL;
ali@416
   316
	}
ali@416
   317
ali@416
   318
	return action;
ali@416
   319
}
ali@416
   320
#endif
ali@416
   321
ali@416
   322
static int
ali@475
   323
move_file(struct razor_atomic *atomic, const char *uri, const char *dest)
ali@416
   324
{
ali@475
   325
	struct razor_error **error;
ali@416
   326
ali@475
   327
	if (atomic->error)
ali@475
   328
		error = NULL;
ali@475
   329
	else
ali@475
   330
		error = &atomic->error;
ali@416
   331
ali@475
   332
	return razor_uri_move(uri, dest, error);
ali@416
   333
}
ali@416
   334
ali@416
   335
static struct atomic_action *
ali@416
   336
atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action)
ali@416
   337
{
ali@416
   338
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   339
		atomic_action_free(action);
ali@416
   340
		return NULL;
ali@416
   341
	}
ali@416
   342
ali@475
   343
	if (move_file(atomic, action->args.uri, action->args.u.move.dest)) {
ali@416
   344
		atomic_action_free(action);
ali@416
   345
		return NULL;
ali@416
   346
	}
ali@416
   347
ali@416
   348
	return action;
ali@416
   349
}
ali@416
   350
ali@416
   351
static struct atomic_action *
ali@416
   352
atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action)
ali@416
   353
{
ali@416
   354
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   355
		atomic_action_free(action);
ali@416
   356
		return NULL;
ali@416
   357
	}
ali@416
   358
ali@475
   359
	if (move_file(atomic, action->args.u.move.dest, action->args.uri)) {
ali@416
   360
		atomic_action_free(action);
ali@416
   361
		return NULL;
ali@416
   362
	}
ali@416
   363
ali@416
   364
	return action;
ali@416
   365
}
ali@416
   366
ali@416
   367
static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic,
ali@416
   368
						 struct atomic_action *action)
ali@416
   369
{
ali@416
   370
	switch(action->type) {
ali@416
   371
	case ACTION_MAKE_DIRS:
ali@416
   372
		action = atomic_action_make_dirs(atomic, action);
ali@416
   373
		break;
ali@416
   374
	case ACTION_REMOVE:
ali@416
   375
		action = atomic_action_remove(atomic, action);
ali@416
   376
		break;
ali@416
   377
	case ACTION_CREATE_DIR:
ali@416
   378
		action = atomic_action_create_dir(atomic, action);
ali@416
   379
		break;
ali@416
   380
	case ACTION_MOVE:
ali@416
   381
		action = atomic_action_move(atomic, action);
ali@416
   382
		break;
ali@416
   383
#if HAVE_SYMLINK
ali@416
   384
	case ACTION_CREATE_SYMLINK:
ali@416
   385
		action = atomic_action_create_symlink(atomic, action);
ali@416
   386
		break;
ali@416
   387
#endif
ali@416
   388
	}
ali@416
   389
	return action;
ali@416
   390
}
ali@416
   391
ali@416
   392
static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic,
ali@416
   393
						   struct atomic_action *action)
ali@416
   394
{
ali@416
   395
	switch(action->type) {
ali@416
   396
	case ACTION_MAKE_DIRS:
ali@416
   397
	case ACTION_REMOVE:
ali@416
   398
		/* Complex actions: should never happen */
ali@416
   399
		break;
ali@416
   400
	case ACTION_CREATE_DIR:
ali@416
   401
		action = atomic_action_rmdir(atomic, action);
ali@416
   402
		break;
ali@416
   403
	case ACTION_MOVE:
ali@416
   404
		action = atomic_action_unmove(atomic, action);
ali@416
   405
		break;
ali@416
   406
#if HAVE_SYMLINK
ali@416
   407
	case ACTION_CREATE_SYMLINK:
ali@416
   408
		action = atomic_action_remove_symlink(atomic, action);
ali@416
   409
		break;
ali@416
   410
#endif
ali@416
   411
	}
ali@416
   412
	return action;
ali@416
   413
}
ali@416
   414
ali@416
   415
/*
ali@416
   416
 * Note that undo has no error checking.
ali@416
   417
 */
ali@416
   418
ali@416
   419
void atomic_action_undo(struct razor_atomic *atomic,
ali@416
   420
			struct atomic_action *actions)
ali@416
   421
{
ali@416
   422
	struct atomic_action *a;
ali@416
   423
ali@416
   424
	atomic->in_undo = 1;
ali@416
   425
ali@416
   426
	while (actions) {
ali@416
   427
		a = atomic_action_list_pop_head(&actions);
ali@416
   428
		a = atomic_action_reverse(atomic, a);
ali@416
   429
		atomic_action_free(a);
ali@416
   430
	}
ali@416
   431
ali@416
   432
	atomic->in_undo = 0;
ali@416
   433
}
ali@416
   434
ali@416
   435
struct atomic_action *atomic_action_do(struct razor_atomic *atomic,
ali@416
   436
				       struct atomic_action *actions)
ali@416
   437
{
ali@416
   438
	struct atomic_action *done = NULL, *a;
ali@416
   439
ali@416
   440
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   441
		atomic_action_free(actions);
ali@416
   442
		return NULL;
ali@416
   443
	}
ali@416
   444
ali@416
   445
	while (actions) {
ali@416
   446
		a = atomic_action_list_pop_head(&actions);
ali@416
   447
		a = atomic_action_apply(atomic, a);
ali@416
   448
		if (a)
ali@416
   449
			done = atomic_action_list_concat(a, done);
ali@416
   450
		if (razor_atomic_in_error_state(atomic)) {
ali@416
   451
			atomic_action_undo(atomic, done);
ali@416
   452
			done = NULL;
ali@444
   453
			atomic_action_free(a);
ali@416
   454
		}
ali@416
   455
	}
ali@416
   456
ali@416
   457
	return done;
ali@416
   458
}
ali@416
   459
ali@416
   460
#endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */