librazor/atomic-actions.c
author J. Ali Harlow <ali@juiblex.co.uk>
Wed Apr 22 13:09:42 2015 +0100 (2015-04-22)
changeset 465 271bb9e6e10f
parent 450 f969505e9265
child 471 a3e5e3eaf224
permissions -rw-r--r--
Add -lole32 to link libraries.

This fixes a problem when compiling with mingw-headers version 3.3
where the use of SHGetFolderPath() expands to a call to CoTaskMemFree()
which is defined in libole32.dll:

/usr/x86_64-w64-mingw32/sys-root/mingw/include/shobjidl.h:29954: undefined reference to `__imp_CoTaskMemFree'
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@458
   193
	atomic_action_free(action);
ali@458
   194
ali@416
   195
	return atomic_action_do(atomic, primitives);
ali@416
   196
}
ali@416
   197
ali@416
   198
static struct atomic_action *
ali@416
   199
atomic_action_remove(struct razor_atomic *atomic, struct atomic_action *action)
ali@416
   200
{
ali@416
   201
#ifdef MSWIN_API
ali@416
   202
	wchar_t *path;
ali@416
   203
	_WDIR *dir;
ali@416
   204
#else
ali@416
   205
	DIR *dir;
ali@416
   206
#endif
ali@416
   207
	struct atomic_action *prim;
ali@416
   208
ali@416
   209
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   210
		atomic_action_free(action);
ali@416
   211
		return NULL;
ali@416
   212
	}
ali@416
   213
ali@416
   214
	/*
ali@416
   215
	 * Non-empty directories should NOT be removed
ali@416
   216
	 */
ali@416
   217
#ifdef MSWIN_API
ali@416
   218
	path = razor_utf8_to_utf16(action->args.path, -1);
ali@416
   219
ali@416
   220
	dir = _wopendir(path);
ali@416
   221
	if (dir && _wreaddir(dir)) {
ali@416
   222
		atomic_action_free(action);
ali@416
   223
		action = NULL;
ali@416
   224
	}
ali@416
   225
	_wclosedir(dir);
ali@416
   226
#else
ali@416
   227
	dir = opendir(action->args.path);
ali@416
   228
	if (dir && readdir(dir)) {
ali@416
   229
		atomic_action_free(action);
ali@416
   230
		action = NULL;
ali@416
   231
	}
ali@416
   232
	closedir(dir);
ali@416
   233
#endif
ali@416
   234
ali@416
   235
	if (action) {
ali@416
   236
		prim = atomic_action_new(ACTION_MOVE);
ali@416
   237
		prim->args.path = strdup(action->args.path);
ali@416
   238
		prim->args.u.move.dest = atomic_action_attic_tmpnam(atomic);
ali@416
   239
ali@416
   240
		atomic_action_free(action);
ali@416
   241
	} else
ali@416
   242
		prim = NULL;
ali@416
   243
ali@416
   244
	return prim ? atomic_action_do(atomic, prim) : NULL;
ali@416
   245
}
ali@416
   246
ali@416
   247
static struct atomic_action *
ali@416
   248
atomic_action_create_dir(struct razor_atomic *atomic,
ali@416
   249
			 struct atomic_action *action)
ali@416
   250
{
ali@416
   251
	mode_t mode;
ali@450
   252
	struct stat buf;
ali@416
   253
ali@416
   254
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   255
		atomic_action_free(action);
ali@416
   256
		return NULL;
ali@416
   257
	}
ali@416
   258
ali@416
   259
	mode = action->args.u.create_dir.mode & (S_IRWXU | S_IRWXG | S_IRWXO);
ali@416
   260
ali@416
   261
	if (!mkdir(action->args.path, mode))
ali@450
   262
		return action;
ali@416
   263
ali@450
   264
	if (errno != EEXIST || stat(action->args.path, &buf)) {
ali@423
   265
		if (!atomic->error)
ali@447
   266
			atomic->error = razor_error_new_posix(action->args.path);
ali@416
   267
		atomic_action_free(action);
ali@416
   268
		return NULL;
ali@416
   269
	}
ali@416
   270
ali@450
   271
	if (!S_ISDIR(buf.st_mode) && !atomic->error)
ali@450
   272
		atomic->error = razor_error_new_str(RAZOR_POSIX_ERROR, EEXIST,
ali@450
   273
						    action->args.path,
ali@450
   274
						    "Not a directory");
ali@450
   275
ali@450
   276
	atomic_action_free(action);
ali@450
   277
	return NULL;
ali@416
   278
}
ali@416
   279
ali@416
   280
static struct atomic_action *atomic_action_rmdir(struct razor_atomic *atomic,
ali@416
   281
						 struct atomic_action *action)
ali@416
   282
{
ali@416
   283
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   284
		atomic_action_free(action);
ali@416
   285
		return NULL;
ali@416
   286
	}
ali@416
   287
ali@416
   288
	if (rmdir(action->args.path) < 0) {
ali@423
   289
		if (!atomic->error)
ali@447
   290
			atomic->error = razor_error_new_posix(action->args.path);
ali@416
   291
		atomic_action_free(action);
ali@416
   292
		return NULL;
ali@416
   293
	} else
ali@416
   294
		return action;
ali@416
   295
}
ali@416
   296
ali@416
   297
#if HAVE_SYMLINK
ali@416
   298
static struct atomic_action *
ali@416
   299
atomic_action_create_symlink(struct razor_atomic *atomic,
ali@416
   300
			     struct atomic_action *action)
ali@416
   301
{
ali@416
   302
	int r;
ali@416
   303
ali@416
   304
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   305
		atomic_action_free(action);
ali@416
   306
		return NULL;
ali@416
   307
	}
ali@416
   308
ali@416
   309
	r = symlink(action->args.u.create_symlink.target, action->args.path);
ali@416
   310
	if (r < 0) {
ali@423
   311
		if (!atomic->error)
ali@447
   312
			atomic->error = razor_error_new_posix(action->args.path);
ali@416
   313
		atomic_action_free(action);
ali@416
   314
		return NULL;
ali@416
   315
	}
ali@416
   316
ali@416
   317
	return action;
ali@416
   318
}
ali@416
   319
ali@416
   320
static struct atomic_action *
ali@416
   321
atomic_action_remove_symlink(struct razor_atomic *atomic,
ali@416
   322
			     struct atomic_action *action)
ali@416
   323
{
ali@416
   324
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   325
		atomic_action_free(action);
ali@416
   326
		return NULL;
ali@416
   327
	}
ali@416
   328
ali@416
   329
	if (unlink(action->args.path) < 0) {
ali@423
   330
		if (!atomic->error)
ali@447
   331
			atomic->error = razor_error_new_posix(action->args.path);
ali@416
   332
		atomic_action_free(action);
ali@416
   333
		return NULL;
ali@416
   334
	}
ali@416
   335
ali@416
   336
	return action;
ali@416
   337
}
ali@416
   338
#endif
ali@416
   339
ali@416
   340
static int
ali@416
   341
move_file(struct razor_atomic *atomic, const char *path, const char *dest)
ali@416
   342
{
ali@416
   343
#ifdef MSWIN_API
ali@416
   344
	wchar_t *oldbuf, *newbuf;
ali@416
   345
	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
ali@416
   346
ali@416
   347
	newbuf = razor_utf8_to_utf16(dest, -1);
ali@416
   348
	oldbuf = razor_utf8_to_utf16(path, -1);
ali@416
   349
ali@416
   350
	/*
ali@416
   351
	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
ali@416
   352
	 * cover every case we care about _except_ replacing an empty
ali@416
   353
	 * directory with a file. Calling RemoveDirectory() will deal
ali@416
   354
	 * with this case while having no effect in all other cases.
ali@416
   355
	 */
ali@416
   356
	(void)RemoveDirectoryW(newbuf);
ali@416
   357
ali@416
   358
	if (!MoveFileExW(oldbuf, newbuf, flags)) {
ali@423
   359
		if (!atomic->error)
ali@423
   360
			atomic->error = razor_error_new_mswin(newbuf,
ali@423
   361
							      GetLastError());
ali@416
   362
		return -1;
ali@416
   363
	}
ali@416
   364
ali@416
   365
	free(newbuf);
ali@416
   366
	free(oldbuf);
ali@416
   367
#else
ali@416
   368
	if (rename(path, dest)) {
ali@423
   369
		if (!atomic->error)
ali@447
   370
			atomic->error = razor_error_new_posix(dest);
ali@416
   371
		return -1;
ali@416
   372
	}
ali@416
   373
#endif
ali@416
   374
ali@416
   375
	return 0;
ali@416
   376
}
ali@416
   377
ali@416
   378
static struct atomic_action *
ali@416
   379
atomic_action_move(struct razor_atomic *atomic, struct atomic_action *action)
ali@416
   380
{
ali@416
   381
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   382
		atomic_action_free(action);
ali@416
   383
		return NULL;
ali@416
   384
	}
ali@416
   385
ali@416
   386
	if (move_file(atomic, action->args.path, action->args.u.move.dest)) {
ali@416
   387
		atomic_action_free(action);
ali@416
   388
		return NULL;
ali@416
   389
	}
ali@416
   390
ali@416
   391
	return action;
ali@416
   392
}
ali@416
   393
ali@416
   394
static struct atomic_action *
ali@416
   395
atomic_action_unmove(struct razor_atomic *atomic, struct atomic_action *action)
ali@416
   396
{
ali@416
   397
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   398
		atomic_action_free(action);
ali@416
   399
		return NULL;
ali@416
   400
	}
ali@416
   401
ali@416
   402
	if (move_file(atomic, action->args.u.move.dest, action->args.path)) {
ali@416
   403
		atomic_action_free(action);
ali@416
   404
		return NULL;
ali@416
   405
	}
ali@416
   406
ali@416
   407
	return action;
ali@416
   408
}
ali@416
   409
ali@416
   410
static struct atomic_action *atomic_action_apply(struct razor_atomic *atomic,
ali@416
   411
						 struct atomic_action *action)
ali@416
   412
{
ali@416
   413
	switch(action->type) {
ali@416
   414
	case ACTION_MAKE_DIRS:
ali@416
   415
		action = atomic_action_make_dirs(atomic, action);
ali@416
   416
		break;
ali@416
   417
	case ACTION_REMOVE:
ali@416
   418
		action = atomic_action_remove(atomic, action);
ali@416
   419
		break;
ali@416
   420
	case ACTION_CREATE_DIR:
ali@416
   421
		action = atomic_action_create_dir(atomic, action);
ali@416
   422
		break;
ali@416
   423
	case ACTION_MOVE:
ali@416
   424
		action = atomic_action_move(atomic, action);
ali@416
   425
		break;
ali@416
   426
#if HAVE_SYMLINK
ali@416
   427
	case ACTION_CREATE_SYMLINK:
ali@416
   428
		action = atomic_action_create_symlink(atomic, action);
ali@416
   429
		break;
ali@416
   430
#endif
ali@416
   431
	}
ali@416
   432
	return action;
ali@416
   433
}
ali@416
   434
ali@416
   435
static struct atomic_action *atomic_action_reverse(struct razor_atomic *atomic,
ali@416
   436
						   struct atomic_action *action)
ali@416
   437
{
ali@416
   438
	switch(action->type) {
ali@416
   439
	case ACTION_MAKE_DIRS:
ali@416
   440
	case ACTION_REMOVE:
ali@416
   441
		/* Complex actions: should never happen */
ali@416
   442
		break;
ali@416
   443
	case ACTION_CREATE_DIR:
ali@416
   444
		action = atomic_action_rmdir(atomic, action);
ali@416
   445
		break;
ali@416
   446
	case ACTION_MOVE:
ali@416
   447
		action = atomic_action_unmove(atomic, action);
ali@416
   448
		break;
ali@416
   449
#if HAVE_SYMLINK
ali@416
   450
	case ACTION_CREATE_SYMLINK:
ali@416
   451
		action = atomic_action_remove_symlink(atomic, action);
ali@416
   452
		break;
ali@416
   453
#endif
ali@416
   454
	}
ali@416
   455
	return action;
ali@416
   456
}
ali@416
   457
ali@416
   458
/*
ali@416
   459
 * Note that undo has no error checking.
ali@416
   460
 */
ali@416
   461
ali@416
   462
void atomic_action_undo(struct razor_atomic *atomic,
ali@416
   463
			struct atomic_action *actions)
ali@416
   464
{
ali@416
   465
	struct atomic_action *a;
ali@416
   466
ali@416
   467
	atomic->in_undo = 1;
ali@416
   468
ali@416
   469
	while (actions) {
ali@416
   470
		a = atomic_action_list_pop_head(&actions);
ali@416
   471
		a = atomic_action_reverse(atomic, a);
ali@416
   472
		atomic_action_free(a);
ali@416
   473
	}
ali@416
   474
ali@416
   475
	atomic->in_undo = 0;
ali@416
   476
}
ali@416
   477
ali@416
   478
struct atomic_action *atomic_action_do(struct razor_atomic *atomic,
ali@416
   479
				       struct atomic_action *actions)
ali@416
   480
{
ali@416
   481
	struct atomic_action *done = NULL, *a;
ali@416
   482
ali@416
   483
	if (razor_atomic_in_error_state(atomic)) {
ali@416
   484
		atomic_action_free(actions);
ali@416
   485
		return NULL;
ali@416
   486
	}
ali@416
   487
ali@416
   488
	while (actions) {
ali@416
   489
		a = atomic_action_list_pop_head(&actions);
ali@416
   490
		a = atomic_action_apply(atomic, a);
ali@416
   491
		if (a)
ali@416
   492
			done = atomic_action_list_concat(a, done);
ali@416
   493
		if (razor_atomic_in_error_state(atomic)) {
ali@416
   494
			atomic_action_undo(atomic, done);
ali@416
   495
			done = NULL;
ali@444
   496
			atomic_action_free(a);
ali@416
   497
		}
ali@416
   498
	}
ali@416
   499
ali@416
   500
	return done;
ali@416
   501
}
ali@416
   502
ali@416
   503
#endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */