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.
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 */