librazor/atomic-emulate.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Jul 08 15:54:49 2016 +0100 (2016-07-08)
changeset 482 6a8a57779674
parent 461 e1b95d57dd54
permissions -rw-r--r--
Release 0.6.3.101
ali@416
     1
/*
ali@475
     2
 * Copyright (C) 2012, 2014, 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@423
    24
#include <stdio.h>
ali@416
    25
#include <string.h>
ali@416
    26
#include <unistd.h>
ali@416
    27
#include <fcntl.h>
ali@416
    28
#include "razor-internal.h"
ali@416
    29
ali@416
    30
/*
ali@416
    31
 * Emulated atomic support
ali@416
    32
 *
ali@416
    33
 * This implementation is better than nothing, but is certainly not atomic.
ali@416
    34
 * It does have a couple of advantages over atomic-none:
ali@416
    35
 *	- If a file operation fails while a package is being installed we
ali@416
    36
 *	  have a good chance of being able to rollback the transaction to
ali@416
    37
 *	  a well-known state.
ali@416
    38
 *	- We behave similarly to atomic-ktm in that changes are not visible
ali@416
    39
 *	  on disk to non-atomic operations (eg., scripts) until the atomic
ali@416
    40
 *	  is committed. This makes the testsuite more likely to pick up
ali@416
    41
 *	  problems that would otherwise only be found when using razor on
ali@416
    42
 *	  an MS-Windows system which supports KTM.
ali@416
    43
 */
ali@416
    44
ali@416
    45
#ifndef O_BINARY
ali@416
    46
#define O_BINARY	0
ali@416
    47
#endif
ali@416
    48
ali@416
    49
static void recursive_remove(const char *directory)
ali@416
    50
{
ali@475
    51
	void *dp;
ali@475
    52
	char *name, *buf;
ali@416
    53
ali@475
    54
	dp = razor_uri_opendir(directory, NULL);
ali@475
    55
ali@475
    56
	if (dp) {
ali@475
    57
		while((name = razor_uri_readdir(dp, NULL))) {
ali@475
    58
			buf = razor_concat(directory, "/", name, NULL);
ali@475
    59
			free(name);
ali@475
    60
			if (razor_uri_unlink(buf, NULL) < 0)
ali@416
    61
				recursive_remove(buf);
ali@416
    62
			free(buf);
ali@416
    63
		}
ali@475
    64
ali@475
    65
		razor_uri_closedir(dp, NULL);
ali@416
    66
	}
ali@416
    67
ali@416
    68
	rmdir(directory);
ali@416
    69
}
ali@416
    70
ali@416
    71
RAZOR_EXPORT struct razor_atomic *razor_atomic_open(const char *description)
ali@416
    72
{
ali@416
    73
	struct razor_atomic *atomic;
ali@416
    74
ali@416
    75
	atomic = zalloc(sizeof *atomic);
ali@416
    76
ali@416
    77
	atomic->description = strdup(description);
ali@416
    78
ali@416
    79
	return atomic;
ali@416
    80
}
ali@416
    81
ali@416
    82
RAZOR_EXPORT int razor_atomic_commit(struct razor_atomic *atomic)
ali@416
    83
{
ali@416
    84
	struct atomic_action *actions;
ali@416
    85
ali@416
    86
	if (razor_atomic_in_error_state(atomic))
ali@416
    87
		return -1;
ali@416
    88
ali@416
    89
	if (atomic->actions) {
ali@416
    90
		actions = atomic_action_list_reverse(atomic->actions);
ali@416
    91
		atomic->actions = NULL;
ali@416
    92
		actions = atomic_action_do(atomic, actions);
ali@416
    93
		atomic_action_free(actions);
ali@416
    94
	}
ali@416
    95
ali@416
    96
	if (atomic->toplevel) {
ali@416
    97
		recursive_remove(atomic->toplevel);
ali@416
    98
		free(atomic->toplevel);
ali@416
    99
		atomic->toplevel = NULL;
ali@416
   100
	}
ali@416
   101
ali@423
   102
	return razor_atomic_in_error_state(atomic);
ali@416
   103
}
ali@416
   104
ali@416
   105
RAZOR_EXPORT void razor_atomic_destroy(struct razor_atomic *atomic)
ali@416
   106
{
ali@458
   107
	if (atomic->actions) {
ali@458
   108
		atomic_action_free(atomic->actions);
ali@458
   109
		atomic->actions = NULL;
ali@458
   110
	}
ali@458
   111
ali@416
   112
	if (atomic->toplevel) {
ali@416
   113
		recursive_remove(atomic->toplevel);
ali@416
   114
		free(atomic->toplevel);
ali@416
   115
		atomic->toplevel = NULL;
ali@416
   116
	}
ali@416
   117
ali@423
   118
	if (atomic->error)
ali@423
   119
		razor_error_free(atomic->error);
ali@423
   120
ali@458
   121
	free(atomic->description);
ali@458
   122
ali@416
   123
	free(atomic);
ali@416
   124
}
ali@416
   125
ali@435
   126
/*
ali@435
   127
 * We need a toplevel directory in which to hold temporary files
ali@435
   128
 * before they are committed. Since we can generally assume that
ali@444
   129
 * we have write permissions anywhere on the filesystem in
ali@444
   130
 * question, the best location is at the relevant mount point.
ali@444
   131
 * The most common case where this assumption fails is when
ali@444
   132
 * testing, when the current directory is a good choice.
ali@435
   133
 */
ali@435
   134
ali@435
   135
static int
ali@475
   136
razor_atomic_set_toplevel_from_uri(struct razor_atomic *atomic,
ali@475
   137
				   const char *uri)
ali@435
   138
{
ali@435
   139
	if (razor_atomic_in_error_state(atomic))
ali@435
   140
		return -1;
ali@435
   141
ali@435
   142
	if (atomic->toplevel)
ali@435
   143
		return 0;
ali@435
   144
ali@435
   145
#ifdef MSWIN_API
ali@475
   146
	atomic->toplevel = razor_uri_mkdtemp_near(uri, "atomic-XXXXXX",
ali@475
   147
						   &atomic->error);
ali@435
   148
#else
ali@475
   149
	atomic->toplevel = razor_uri_mkdtemp_near(uri, ".atomic-XXXXXX",
ali@475
   150
						   &atomic->error);
ali@435
   151
#endif
ali@435
   152
ali@435
   153
	return !atomic->toplevel;
ali@435
   154
}
ali@435
   155
ali@475
   156
/*
ali@475
   157
 * If root_uri is empty, then uri can be a URI (but not a relative-ref; ie.,
ali@475
   158
 * a scheme must be specified). If root_uri is non-empty, then it must be a
ali@475
   159
 * URI (but not a relative-ref) and uri must be a non-empty path.
ali@475
   160
 */
ali@416
   161
RAZOR_EXPORT int
ali@475
   162
razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root_uri,
ali@475
   163
		       const char *uri)
ali@416
   164
{
ali@416
   165
	struct atomic_action *a;
ali@475
   166
	struct razor_uri ru;
ali@475
   167
	struct razor_error *tmp_error = NULL;
ali@475
   168
	char *fakeroot = NULL;
ali@416
   169
ali@475
   170
	razor_atomic_set_toplevel_from_uri(atomic, *root_uri ? root_uri : uri);
ali@435
   171
ali@416
   172
	if (razor_atomic_in_error_state(atomic))
ali@416
   173
		return -1;
ali@416
   174
ali@475
   175
	if (!*root_uri) {
ali@475
   176
		if (razor_uri_parse(&ru, uri, &tmp_error)) {
ali@475
   177
			razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@475
   178
			return -1;
ali@475
   179
		}
ali@475
   180
		if (*ru.path == '/') {
ali@475
   181
			free(ru.path);
ali@475
   182
			ru.path = strdup("/");
ali@475
   183
		} else {
ali@475
   184
			free(ru.path);
ali@475
   185
			ru.path = strdup("");
ali@475
   186
		}
ali@475
   187
		fakeroot = razor_uri_recompose(&ru);
ali@475
   188
		razor_uri_destroy(&ru);
ali@475
   189
		root_uri = fakeroot;
ali@475
   190
		uri += strlen(fakeroot);
ali@475
   191
	}
ali@475
   192
ali@416
   193
	a = atomic_action_new(ACTION_MAKE_DIRS);
ali@475
   194
	a->args.uri = strdup(uri);
ali@475
   195
	a->args.u.make_dirs.root = strdup(root_uri);
ali@475
   196
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@475
   197
ali@475
   198
	if (fakeroot)
ali@475
   199
		free(fakeroot);
ali@475
   200
ali@475
   201
	return 0;
ali@475
   202
}
ali@475
   203
ali@475
   204
RAZOR_EXPORT int
ali@475
   205
razor_atomic_remove(struct razor_atomic *atomic, const char *uri)
ali@475
   206
{
ali@475
   207
	struct atomic_action *a;
ali@475
   208
ali@475
   209
	razor_atomic_set_toplevel_from_uri(atomic, uri);
ali@475
   210
ali@475
   211
	if (razor_atomic_in_error_state(atomic))
ali@475
   212
		return -1;
ali@475
   213
ali@475
   214
	a = atomic_action_new(ACTION_REMOVE);
ali@475
   215
	a->args.uri = strdup(uri);
ali@416
   216
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   217
ali@416
   218
	return 0;
ali@416
   219
}
ali@416
   220
ali@416
   221
RAZOR_EXPORT int
ali@475
   222
razor_atomic_rename_file(struct razor_atomic *atomic, const char *old_uri,
ali@475
   223
			 const char *new_uri)
ali@416
   224
{
ali@416
   225
	struct atomic_action *a;
ali@416
   226
ali@475
   227
	razor_atomic_set_toplevel_from_uri(atomic, new_uri);
ali@435
   228
ali@416
   229
	if (razor_atomic_in_error_state(atomic))
ali@416
   230
		return -1;
ali@416
   231
ali@475
   232
	a = atomic_action_new(ACTION_MOVE);
ali@475
   233
	a->args.uri = strdup(old_uri);
ali@475
   234
	a->args.u.move.dest = strdup(new_uri);
ali@416
   235
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   236
ali@416
   237
	return 0;
ali@416
   238
}
ali@416
   239
ali@416
   240
RAZOR_EXPORT int
ali@475
   241
razor_atomic_create_dir(struct razor_atomic *atomic, const char *uri,
ali@416
   242
			mode_t mode)
ali@416
   243
{
ali@416
   244
	struct atomic_action *a;
ali@416
   245
ali@475
   246
	razor_atomic_set_toplevel_from_uri(atomic, uri);
ali@435
   247
ali@416
   248
	if (razor_atomic_in_error_state(atomic))
ali@416
   249
		return -1;
ali@416
   250
ali@416
   251
	a = atomic_action_new(ACTION_CREATE_DIR);
ali@475
   252
	a->args.uri = strdup(uri);
ali@416
   253
	a->args.u.create_dir.mode = mode;
ali@416
   254
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   255
ali@416
   256
	return 0;
ali@416
   257
}
ali@416
   258
ali@416
   259
RAZOR_EXPORT int
ali@416
   260
razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
ali@475
   261
			    const char *uri)
ali@416
   262
{
ali@416
   263
#if HAVE_SYMLINK
ali@416
   264
	struct atomic_action *a;
ali@435
   265
ali@475
   266
	razor_atomic_set_toplevel_from_uri(atomic, uri);
ali@416
   267
#endif
ali@416
   268
ali@416
   269
	if (razor_atomic_in_error_state(atomic))
ali@416
   270
		return -1;
ali@416
   271
ali@416
   272
#if HAVE_SYMLINK
ali@416
   273
	a = atomic_action_new(ACTION_CREATE_SYMLINK);
ali@475
   274
	a->args.uri = strdup(uri);
ali@416
   275
	a->args.u.create_symlink.target = strdup(target);
ali@416
   276
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   277
ali@416
   278
	return 0;
ali@416
   279
#else
ali@447
   280
	atomic->error = razor_error_new_str(RAZOR_POSIX_ERROR, ENOSYS, NULL,
ali@423
   281
					    "Symbolic links not supported "
ali@423
   282
					    "on this platform");
ali@416
   283
ali@416
   284
	return -1;
ali@416
   285
#endif
ali@416
   286
}
ali@416
   287
ali@416
   288
RAZOR_EXPORT int
ali@475
   289
razor_atomic_create_file(struct razor_atomic *atomic, const char *uri,
ali@416
   290
                         mode_t mode)
ali@416
   291
{
ali@416
   292
	int fd;
ali@416
   293
	struct atomic_action *a;
ali@416
   294
	char *tmpnam;
ali@416
   295
ali@475
   296
	razor_atomic_set_toplevel_from_uri(atomic, uri);
ali@435
   297
ali@416
   298
	if (razor_atomic_in_error_state(atomic))
ali@416
   299
		return -1;
ali@416
   300
ali@416
   301
	tmpnam = atomic_action_attic_tmpnam(atomic);
ali@475
   302
	fd = razor_uri_open(tmpnam, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
ali@475
   303
			    mode & (S_IRWXU | S_IRWXG | S_IRWXO),
ali@475
   304
			    &atomic->error);
ali@416
   305
ali@475
   306
	if (fd >= 0) {
ali@416
   307
		a = atomic_action_new(ACTION_MOVE);
ali@475
   308
		a->args.uri = tmpnam;
ali@475
   309
		a->args.u.move.dest = strdup(uri);
ali@416
   310
		atomic->actions = atomic_action_list_prepend(atomic->actions,
ali@416
   311
							     a);
ali@416
   312
	}
ali@416
   313
ali@416
   314
	return fd;
ali@416
   315
}
ali@416
   316
ali@416
   317
#endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */