librazor/atomic-emulate.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Aug 23 16:28:31 2014 +0100 (2014-08-23)
changeset 442 c4bcba8023a9
parent 423 6112bcc5d1cf
child 444 a2908416b7cc
permissions -rw-r--r--
Fix compiler warnings
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@423
    24
#include <stdio.h>
ali@416
    25
#include <string.h>
ali@416
    26
#include <unistd.h>
ali@416
    27
#include <sys/types.h>
ali@416
    28
#include <sys/stat.h>
ali@416
    29
#include <fcntl.h>
ali@416
    30
#include <dirent.h>
ali@416
    31
#include <errno.h>
ali@416
    32
#include "razor-internal.h"
ali@416
    33
ali@416
    34
/*
ali@416
    35
 * Emulated atomic support
ali@416
    36
 *
ali@416
    37
 * This implementation is better than nothing, but is certainly not atomic.
ali@416
    38
 * It does have a couple of advantages over atomic-none:
ali@416
    39
 *	- If a file operation fails while a package is being installed we
ali@416
    40
 *	  have a good chance of being able to rollback the transaction to
ali@416
    41
 *	  a well-known state.
ali@416
    42
 *	- We behave similarly to atomic-ktm in that changes are not visible
ali@416
    43
 *	  on disk to non-atomic operations (eg., scripts) until the atomic
ali@416
    44
 *	  is committed. This makes the testsuite more likely to pick up
ali@416
    45
 *	  problems that would otherwise only be found when using razor on
ali@416
    46
 *	  an MS-Windows system which supports KTM.
ali@416
    47
 */
ali@416
    48
ali@416
    49
#ifndef O_BINARY
ali@416
    50
#define O_BINARY	0
ali@416
    51
#endif
ali@416
    52
ali@416
    53
static void recursive_remove(const char *directory)
ali@416
    54
{
ali@416
    55
	DIR *dp;
ali@416
    56
	struct dirent *dirp;
ali@416
    57
	char *buf;
ali@416
    58
ali@416
    59
	dp = opendir(directory);
ali@416
    60
	while((dirp = readdir(dp))) {
ali@416
    61
		if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) {
ali@416
    62
			buf = malloc(strlen(directory) + strlen(dirp->d_name)
ali@416
    63
				     + 2);
ali@416
    64
			sprintf(buf, "%s/%s", directory, dirp->d_name);
ali@416
    65
			if (remove(buf) < 0)
ali@416
    66
				recursive_remove(buf);
ali@416
    67
			free(buf);
ali@416
    68
		}
ali@416
    69
	}
ali@416
    70
ali@416
    71
	rmdir(directory);
ali@416
    72
}
ali@416
    73
ali@416
    74
RAZOR_EXPORT struct razor_atomic *razor_atomic_open(const char *description)
ali@416
    75
{
ali@416
    76
	struct razor_atomic *atomic;
ali@416
    77
ali@416
    78
	atomic = zalloc(sizeof *atomic);
ali@416
    79
ali@416
    80
	atomic->description = strdup(description);
ali@416
    81
ali@416
    82
	return atomic;
ali@416
    83
}
ali@416
    84
ali@416
    85
RAZOR_EXPORT int razor_atomic_commit(struct razor_atomic *atomic)
ali@416
    86
{
ali@416
    87
	struct atomic_action *actions;
ali@416
    88
ali@416
    89
	if (razor_atomic_in_error_state(atomic))
ali@416
    90
		return -1;
ali@416
    91
ali@416
    92
	if (atomic->actions) {
ali@416
    93
		actions = atomic_action_list_reverse(atomic->actions);
ali@416
    94
		atomic->actions = NULL;
ali@416
    95
		actions = atomic_action_do(atomic, actions);
ali@416
    96
		atomic_action_free(actions);
ali@416
    97
	}
ali@416
    98
ali@416
    99
	if (atomic->toplevel) {
ali@416
   100
		recursive_remove(atomic->toplevel);
ali@416
   101
		free(atomic->toplevel);
ali@416
   102
		atomic->toplevel = NULL;
ali@416
   103
	}
ali@416
   104
ali@423
   105
	return razor_atomic_in_error_state(atomic);
ali@416
   106
}
ali@416
   107
ali@416
   108
RAZOR_EXPORT void razor_atomic_destroy(struct razor_atomic *atomic)
ali@416
   109
{
ali@416
   110
	if (atomic->toplevel) {
ali@416
   111
		recursive_remove(atomic->toplevel);
ali@416
   112
		free(atomic->toplevel);
ali@416
   113
		atomic->toplevel = NULL;
ali@416
   114
	}
ali@416
   115
ali@423
   116
	if (atomic->error)
ali@423
   117
		razor_error_free(atomic->error);
ali@423
   118
ali@416
   119
	free(atomic);
ali@416
   120
}
ali@416
   121
ali@435
   122
/*
ali@435
   123
 * We need a toplevel directory in which to hold temporary files
ali@435
   124
 * before they are committed. Since we can generally assume that
ali@435
   125
 * we have write permissions anywhere on the disk in question,
ali@435
   126
 * the best location is in the relevant root directory. The most
ali@435
   127
 * common case where this assumption fails is when testing, when
ali@435
   128
 * the current directory is a good choice.
ali@435
   129
 * It might be even better to find a mount point above path instead
ali@435
   130
 * but this is hard to do, and probably not worth the effort.
ali@435
   131
 */
ali@435
   132
ali@435
   133
static int
ali@435
   134
razor_atomic_set_toplevel_from_path(struct razor_atomic *atomic,
ali@435
   135
				    const char *path)
ali@435
   136
{
ali@435
   137
	if (razor_atomic_in_error_state(atomic))
ali@435
   138
		return -1;
ali@435
   139
ali@435
   140
	if (atomic->toplevel)
ali@435
   141
		return 0;
ali@435
   142
ali@435
   143
#ifdef MSWIN_API
ali@435
   144
	if (path[0]=='\\' && path[1]=='\\' && path[2] && path[2]!='\\'
ali@435
   145
	    && strchr(path+3,'\\')) {
ali@435
   146
		/* We have a UNC path: \\servername\sharename... */
ali@435
   147
		const char *sharename, *root;
ali@435
   148
		int disklen;
ali@435
   149
ali@435
   150
		sharename = strchr(path+3,'\\')+1;
ali@435
   151
		root = strchr(sharename,'\\');
ali@435
   152
		if (root)
ali@435
   153
		    disklen = root - path;
ali@435
   154
		else
ali@435
   155
		    disklen = strlen(path);
ali@435
   156
ali@435
   157
		atomic->toplevel =
ali@435
   158
		  malloc(disklen + strlen("\\atomic-XXXXXX") + 1);
ali@435
   159
		memcpy(atomic->toplevel, path, disklen);
ali@435
   160
		strcpy(atomic->toplevel + disklen, "\\atomic-XXXXXX");
ali@435
   161
	} else if ((*path>='A' && *path<='Z' || *path>='a' && *path<='z') &&
ali@435
   162
		    path[1]==':') {
ali@435
   163
		atomic->toplevel = strdup("X:\\atomic-XXXXXX");
ali@435
   164
		*atomic->toplevel = *path;
ali@435
   165
	} else {
ali@435
   166
		DWORD n;
ali@435
   167
		wchar_t *buf;
ali@435
   168
		char *dir;
ali@435
   169
ali@435
   170
		n = GetCurrentDirectoryW(0, NULL);
ali@435
   171
		buf = malloc(n * sizeof(wchar_t));
ali@435
   172
ali@435
   173
		if (GetCurrentDirectoryW(n, buf)) {
ali@435
   174
			dir = razor_utf16_to_utf8(buf, n - 1);
ali@435
   175
			razor_atomic_set_toplevel_from_path(atomic, dir);
ali@435
   176
ali@435
   177
			free(dir);
ali@435
   178
			free(buf);
ali@435
   179
			return;
ali@435
   180
		} else
ali@435
   181
			atomic->toplevel = strdup("C:\\atomic-XXXXXX");
ali@435
   182
ali@435
   183
		free(buf);
ali@435
   184
	}
ali@435
   185
#else
ali@435
   186
	atomic->toplevel = strdup("/.atomic-XXXXXX");
ali@435
   187
#endif
ali@435
   188
ali@435
   189
	if (!mkdtemp(atomic->toplevel)) {
ali@435
   190
		int err = errno;
ali@435
   191
ali@435
   192
#ifdef EACCES
ali@435
   193
		if (err == EACCES) {
ali@435
   194
			char *s = strdup("atomic-XXXXXX");
ali@435
   195
ali@435
   196
			if (mkdtemp(s)) {
ali@435
   197
				free(atomic->toplevel);
ali@435
   198
				atomic->toplevel = s;
ali@435
   199
				return 0;
ali@435
   200
			} else
ali@435
   201
				free(s);
ali@435
   202
		}
ali@435
   203
#endif
ali@435
   204
ali@435
   205
		atomic->error = razor_error_new_str(atomic->toplevel,
ali@435
   206
						    strerror(err));
ali@435
   207
ali@435
   208
		free(atomic->toplevel);
ali@435
   209
		atomic->toplevel = NULL;
ali@435
   210
	}
ali@435
   211
ali@435
   212
	return !atomic->toplevel;
ali@435
   213
}
ali@435
   214
ali@416
   215
RAZOR_EXPORT int
ali@416
   216
razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
ali@416
   217
		       const char *path)
ali@416
   218
{
ali@416
   219
	struct atomic_action *a;
ali@416
   220
ali@435
   221
	razor_atomic_set_toplevel_from_path(atomic, *root ? root : path);
ali@435
   222
ali@416
   223
	if (razor_atomic_in_error_state(atomic))
ali@416
   224
		return -1;
ali@416
   225
ali@416
   226
	a = atomic_action_new(ACTION_MAKE_DIRS);
ali@416
   227
	a->args.path = strdup(path);
ali@416
   228
	a->args.u.make_dirs.root = strdup(root);
ali@416
   229
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   230
ali@416
   231
	return 0;
ali@416
   232
}
ali@416
   233
ali@416
   234
RAZOR_EXPORT int
ali@416
   235
razor_atomic_remove(struct razor_atomic *atomic, const char *path)
ali@416
   236
{
ali@416
   237
	struct atomic_action *a;
ali@416
   238
ali@435
   239
	razor_atomic_set_toplevel_from_path(atomic, path);
ali@435
   240
ali@416
   241
	if (razor_atomic_in_error_state(atomic))
ali@416
   242
		return -1;
ali@416
   243
ali@416
   244
	a = atomic_action_new(ACTION_REMOVE);
ali@416
   245
	a->args.path = strdup(path);
ali@416
   246
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   247
ali@416
   248
	return 0;
ali@416
   249
}
ali@416
   250
ali@416
   251
RAZOR_EXPORT int
ali@416
   252
razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
ali@416
   253
			 const char *newpath)
ali@416
   254
{
ali@416
   255
	struct atomic_action *a;
ali@416
   256
ali@435
   257
	razor_atomic_set_toplevel_from_path(atomic, newpath);
ali@435
   258
ali@416
   259
	if (razor_atomic_in_error_state(atomic))
ali@416
   260
		return -1;
ali@416
   261
ali@416
   262
	a = atomic_action_new(ACTION_MOVE);
ali@416
   263
	a->args.path = strdup(oldpath);
ali@416
   264
	a->args.u.move.dest = strdup(newpath);
ali@416
   265
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   266
ali@416
   267
	return 0;
ali@416
   268
}
ali@416
   269
ali@416
   270
RAZOR_EXPORT int
ali@416
   271
razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
ali@416
   272
			mode_t mode)
ali@416
   273
{
ali@416
   274
	struct atomic_action *a;
ali@416
   275
ali@435
   276
	razor_atomic_set_toplevel_from_path(atomic, dirname);
ali@435
   277
ali@416
   278
	if (razor_atomic_in_error_state(atomic))
ali@416
   279
		return -1;
ali@416
   280
ali@416
   281
	a = atomic_action_new(ACTION_CREATE_DIR);
ali@416
   282
	a->args.path = strdup(dirname);
ali@416
   283
	a->args.u.create_dir.mode = mode;
ali@416
   284
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   285
ali@416
   286
	return 0;
ali@416
   287
}
ali@416
   288
ali@416
   289
RAZOR_EXPORT int
ali@416
   290
razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
ali@416
   291
			    const char *path)
ali@416
   292
{
ali@416
   293
#if HAVE_SYMLINK
ali@416
   294
	struct atomic_action *a;
ali@435
   295
ali@435
   296
	razor_atomic_set_toplevel_from_path(atomic, path);
ali@416
   297
#endif
ali@416
   298
ali@416
   299
	if (razor_atomic_in_error_state(atomic))
ali@416
   300
		return -1;
ali@416
   301
ali@416
   302
#if HAVE_SYMLINK
ali@416
   303
	a = atomic_action_new(ACTION_CREATE_SYMLINK);
ali@416
   304
	a->args.path = strdup(path);
ali@416
   305
	a->args.u.create_symlink.target = strdup(target);
ali@416
   306
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   307
ali@416
   308
	return 0;
ali@416
   309
#else
ali@423
   310
	atomic->error = razor_error_new_str(NULL,
ali@423
   311
					    "Symbolic links not supported "
ali@423
   312
					    "on this platform");
ali@416
   313
ali@416
   314
	return -1;
ali@416
   315
#endif
ali@416
   316
}
ali@416
   317
ali@416
   318
RAZOR_EXPORT int
ali@416
   319
razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
ali@416
   320
                         mode_t mode)
ali@416
   321
{
ali@416
   322
	int fd;
ali@416
   323
	struct atomic_action *a;
ali@416
   324
	char *tmpnam;
ali@416
   325
ali@435
   326
	razor_atomic_set_toplevel_from_path(atomic, filename);
ali@435
   327
ali@416
   328
	if (razor_atomic_in_error_state(atomic))
ali@416
   329
		return -1;
ali@416
   330
ali@416
   331
	tmpnam = atomic_action_attic_tmpnam(atomic);
ali@416
   332
	fd = open(tmpnam, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
ali@416
   333
		  mode & (S_IRWXU | S_IRWXG | S_IRWXO));
ali@416
   334
ali@416
   335
	if (fd == -1)
ali@423
   336
		atomic->error = razor_error_new_str(filename, strerror(errno));
ali@416
   337
	else {
ali@416
   338
		a = atomic_action_new(ACTION_MOVE);
ali@416
   339
		a->args.path = tmpnam;
ali@416
   340
		a->args.u.move.dest = strdup(filename);
ali@416
   341
		atomic->actions = atomic_action_list_prepend(atomic->actions,
ali@416
   342
							     a);
ali@416
   343
	}
ali@416
   344
ali@416
   345
	return fd;
ali@416
   346
}
ali@416
   347
ali@416
   348
#endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */