librazor/atomic-emulate.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Aug 23 11:13:48 2014 +0100 (2014-08-23)
changeset 440 48204dea0b9f
parent 423 6112bcc5d1cf
child 444 a2908416b7cc
permissions -rw-r--r--
Remove INTLLIBS from librazor_la_LIBADD.

This partially reverts 611c84a3f4b4538a65d186050608c17adbf17770.
It's not clear what motivated the initial inclusion of INTLLIBS
here since the net effect is only seen in librazor.la and not
in razor.pc and librazor.la is not normally packaged. Certainly
neither the static nor the dynamic versions of librazor currently
use libintl. At best this would cause the linker to search a
static libintl for undefined symbols without finding any; at worse
it causes a static build of plover using librazor.la to fail if
no static version of libintl is installed.
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 */