librazor/atomic-emulate.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Oct 03 15:05:33 2014 +0100 (2014-10-03)
changeset 453 1fd1d221092d
parent 447 0a5e583393e1
child 458 3f841a46eab5
permissions -rw-r--r--
Add INTLLIBS back into librazor_la_LIBADD and also into razor.pc

INTLLIBS was originally added into librazor_la_LIBADD (but not razor.pc)
in 611c84a3f4b4538a65d186050608c17adbf17770 and then removed in
236cfc8ecc5c66288e41bf9b72051a3b18326e1c over concerns that it meant
that building static applications (eg., in plover) required a static
version of libintl. Since then, however, the inclusion of gnulib's
getopt has caused the build to fail without INTLLIBS in librazor_la_LIBADD
with messages of the form:

../gl/.libs/libgnu.a(getopt.o): In function `_getopt_internal_r':
/home/ali/wk/razor/vpath/gl/../../working/gl/getopt.c:808: undefined reference to `libintl_gettext'

while building librazor.dll.a

Thus it appears that INTLLIBS really is needed and should go back into
librazor_la_LIBADD (which fixes the build) and should also be in Libs.private
in razor.pc for static builds. This means that a static version of libintl
will be required (eg., as part of mingw64-gettext-static).

Along the way, razor.pc has also been fixed to avoid overlinking
(https://wiki.openmandriva.org/en/Overlinking_issues_in_packaging)
and a missing include directory that caused the razor executable to
fail to build was added.
ali@416
     1
/*
ali@444
     2
 * Copyright (C) 2012, 2014  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@444
    32
#include <unistd.h>
ali@416
    33
#include "razor-internal.h"
ali@416
    34
ali@416
    35
/*
ali@416
    36
 * Emulated atomic support
ali@416
    37
 *
ali@416
    38
 * This implementation is better than nothing, but is certainly not atomic.
ali@416
    39
 * It does have a couple of advantages over atomic-none:
ali@416
    40
 *	- If a file operation fails while a package is being installed we
ali@416
    41
 *	  have a good chance of being able to rollback the transaction to
ali@416
    42
 *	  a well-known state.
ali@416
    43
 *	- We behave similarly to atomic-ktm in that changes are not visible
ali@416
    44
 *	  on disk to non-atomic operations (eg., scripts) until the atomic
ali@416
    45
 *	  is committed. This makes the testsuite more likely to pick up
ali@416
    46
 *	  problems that would otherwise only be found when using razor on
ali@416
    47
 *	  an MS-Windows system which supports KTM.
ali@416
    48
 */
ali@416
    49
ali@416
    50
#ifndef O_BINARY
ali@416
    51
#define O_BINARY	0
ali@416
    52
#endif
ali@416
    53
ali@416
    54
static void recursive_remove(const char *directory)
ali@416
    55
{
ali@416
    56
	DIR *dp;
ali@416
    57
	struct dirent *dirp;
ali@416
    58
	char *buf;
ali@416
    59
ali@416
    60
	dp = opendir(directory);
ali@416
    61
	while((dirp = readdir(dp))) {
ali@416
    62
		if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) {
ali@416
    63
			buf = malloc(strlen(directory) + strlen(dirp->d_name)
ali@416
    64
				     + 2);
ali@416
    65
			sprintf(buf, "%s/%s", directory, dirp->d_name);
ali@416
    66
			if (remove(buf) < 0)
ali@416
    67
				recursive_remove(buf);
ali@416
    68
			free(buf);
ali@416
    69
		}
ali@416
    70
	}
ali@416
    71
ali@416
    72
	rmdir(directory);
ali@416
    73
}
ali@416
    74
ali@416
    75
RAZOR_EXPORT struct razor_atomic *razor_atomic_open(const char *description)
ali@416
    76
{
ali@416
    77
	struct razor_atomic *atomic;
ali@416
    78
ali@416
    79
	atomic = zalloc(sizeof *atomic);
ali@416
    80
ali@416
    81
	atomic->description = strdup(description);
ali@416
    82
ali@416
    83
	return atomic;
ali@416
    84
}
ali@416
    85
ali@416
    86
RAZOR_EXPORT int razor_atomic_commit(struct razor_atomic *atomic)
ali@416
    87
{
ali@416
    88
	struct atomic_action *actions;
ali@416
    89
ali@416
    90
	if (razor_atomic_in_error_state(atomic))
ali@416
    91
		return -1;
ali@416
    92
ali@416
    93
	if (atomic->actions) {
ali@416
    94
		actions = atomic_action_list_reverse(atomic->actions);
ali@416
    95
		atomic->actions = NULL;
ali@416
    96
		actions = atomic_action_do(atomic, actions);
ali@416
    97
		atomic_action_free(actions);
ali@416
    98
	}
ali@416
    99
ali@416
   100
	if (atomic->toplevel) {
ali@416
   101
		recursive_remove(atomic->toplevel);
ali@416
   102
		free(atomic->toplevel);
ali@416
   103
		atomic->toplevel = NULL;
ali@416
   104
	}
ali@416
   105
ali@423
   106
	return razor_atomic_in_error_state(atomic);
ali@416
   107
}
ali@416
   108
ali@416
   109
RAZOR_EXPORT void razor_atomic_destroy(struct razor_atomic *atomic)
ali@416
   110
{
ali@416
   111
	if (atomic->toplevel) {
ali@416
   112
		recursive_remove(atomic->toplevel);
ali@416
   113
		free(atomic->toplevel);
ali@416
   114
		atomic->toplevel = NULL;
ali@416
   115
	}
ali@416
   116
ali@423
   117
	if (atomic->error)
ali@423
   118
		razor_error_free(atomic->error);
ali@423
   119
ali@416
   120
	free(atomic);
ali@416
   121
}
ali@416
   122
ali@444
   123
#ifndef MSWIN_API
ali@444
   124
static char *absolute_path(const char *path)
ali@444
   125
{
ali@444
   126
	int len;
ali@444
   127
	char *result, *subpath, *p, *s, *t;
ali@444
   128
ali@444
   129
	result = realpath(path, NULL);
ali@444
   130
ali@444
   131
	if (!result && errno == ENOENT) {
ali@444
   132
		p = strdup(path);
ali@444
   133
		s = strrchr(p, '/');
ali@444
   134
ali@444
   135
		while (s) {
ali@444
   136
			if (s == p) {
ali@444
   137
				result = strdup("/");
ali@444
   138
				break;
ali@444
   139
			}
ali@444
   140
ali@444
   141
			*s = '\0';
ali@444
   142
			subpath = realpath(p, NULL);
ali@444
   143
ali@444
   144
			if (subpath) {
ali@444
   145
				*s = '/';
ali@444
   146
				len = strlen(subpath);
ali@444
   147
				result = malloc(len + strlen(s) + 1);
ali@444
   148
				memcpy(result, subpath, len);
ali@444
   149
				strcpy(result + len, s);
ali@444
   150
				break;
ali@444
   151
			} else if (errno != ENOENT)
ali@444
   152
				break;
ali@444
   153
ali@444
   154
			t = strrchr(p, '/');
ali@444
   155
			*s = '/';
ali@444
   156
			s = t;
ali@444
   157
		}
ali@444
   158
ali@444
   159
		if (!s)
ali@444
   160
			result = realpath(".", NULL);
ali@444
   161
ali@444
   162
		free(p);
ali@444
   163
	}
ali@444
   164
ali@444
   165
	return result;
ali@444
   166
}
ali@444
   167
#endif
ali@444
   168
ali@435
   169
/*
ali@435
   170
 * We need a toplevel directory in which to hold temporary files
ali@435
   171
 * before they are committed. Since we can generally assume that
ali@444
   172
 * we have write permissions anywhere on the filesystem in
ali@444
   173
 * question, the best location is at the relevant mount point.
ali@444
   174
 * The most common case where this assumption fails is when
ali@444
   175
 * testing, when the current directory is a good choice.
ali@435
   176
 */
ali@435
   177
ali@435
   178
static int
ali@435
   179
razor_atomic_set_toplevel_from_path(struct razor_atomic *atomic,
ali@435
   180
				    const char *path)
ali@435
   181
{
ali@444
   182
#ifndef MSWIN_API
ali@444
   183
	dev_t filesystem;
ali@444
   184
	struct stat buf;
ali@444
   185
#endif
ali@444
   186
ali@435
   187
	if (razor_atomic_in_error_state(atomic))
ali@435
   188
		return -1;
ali@435
   189
ali@435
   190
	if (atomic->toplevel)
ali@435
   191
		return 0;
ali@435
   192
ali@435
   193
#ifdef MSWIN_API
ali@435
   194
	if (path[0]=='\\' && path[1]=='\\' && path[2] && path[2]!='\\'
ali@435
   195
	    && strchr(path+3,'\\')) {
ali@435
   196
		/* We have a UNC path: \\servername\sharename... */
ali@435
   197
		const char *sharename, *root;
ali@435
   198
		int disklen;
ali@435
   199
ali@435
   200
		sharename = strchr(path+3,'\\')+1;
ali@435
   201
		root = strchr(sharename,'\\');
ali@435
   202
		if (root)
ali@435
   203
		    disklen = root - path;
ali@435
   204
		else
ali@435
   205
		    disklen = strlen(path);
ali@435
   206
ali@435
   207
		atomic->toplevel =
ali@435
   208
		  malloc(disklen + strlen("\\atomic-XXXXXX") + 1);
ali@435
   209
		memcpy(atomic->toplevel, path, disklen);
ali@435
   210
		strcpy(atomic->toplevel + disklen, "\\atomic-XXXXXX");
ali@435
   211
	} else if ((*path>='A' && *path<='Z' || *path>='a' && *path<='z') &&
ali@435
   212
		    path[1]==':') {
ali@435
   213
		atomic->toplevel = strdup("X:\\atomic-XXXXXX");
ali@435
   214
		*atomic->toplevel = *path;
ali@435
   215
	} else {
ali@435
   216
		DWORD n;
ali@435
   217
		wchar_t *buf;
ali@435
   218
		char *dir;
ali@435
   219
ali@435
   220
		n = GetCurrentDirectoryW(0, NULL);
ali@435
   221
		buf = malloc(n * sizeof(wchar_t));
ali@435
   222
ali@435
   223
		if (GetCurrentDirectoryW(n, buf)) {
ali@435
   224
			dir = razor_utf16_to_utf8(buf, n - 1);
ali@435
   225
			razor_atomic_set_toplevel_from_path(atomic, dir);
ali@435
   226
ali@435
   227
			free(dir);
ali@435
   228
			free(buf);
ali@435
   229
			return;
ali@435
   230
		} else
ali@435
   231
			atomic->toplevel = strdup("C:\\atomic-XXXXXX");
ali@435
   232
ali@435
   233
		free(buf);
ali@435
   234
	}
ali@435
   235
#else
ali@444
   236
	{
ali@444
   237
		/*
ali@444
   238
		 * Find the mount point (assuming we can write to the
ali@444
   239
		 * whole filesystem). Otherwise stop at the first
ali@444
   240
		 * unwritable directory and take one step back.
ali@444
   241
		 */
ali@444
   242
		char *s, *abspath, saved;
ali@449
   243
		int len, can_step_back = 0;
ali@444
   244
ali@444
   245
		abspath = absolute_path(path);
ali@444
   246
		if (!abspath) {
ali@447
   247
			atomic->error = razor_error_new_posix(path);
ali@444
   248
			return -1;
ali@444
   249
		}
ali@444
   250
ali@444
   251
		if (stat(abspath, &buf) < 0) {
ali@449
   252
			if (errno == ENOENT)
ali@449
   253
				filesystem = 0;
ali@449
   254
			else {
ali@449
   255
				atomic->error = razor_error_new_posix(abspath);
ali@449
   256
				free(abspath);
ali@449
   257
				return -1;
ali@449
   258
			}
ali@449
   259
		} else
ali@449
   260
			filesystem = buf.st_dev;
ali@444
   261
ali@444
   262
		len = strlen(abspath);
ali@444
   263
		while(len > 1 && (s = strrchr(abspath, '/'))) {
ali@444
   264
			if (s == abspath) {
ali@444
   265
				saved = s[1];
ali@444
   266
				s[1] = '\0';
ali@444
   267
				len = s + 1 - abspath;
ali@444
   268
			} else {
ali@444
   269
				s[0] = '\0';
ali@444
   270
				len = s - abspath;
ali@444
   271
			}
ali@444
   272
ali@444
   273
			if (stat(abspath, &buf) < 0) {
ali@449
   274
				if (errno == ENOENT)
ali@449
   275
					continue;
ali@449
   276
				else {
ali@449
   277
				    atomic->error = razor_error_new_posix(abspath);
ali@449
   278
				    free(abspath);
ali@449
   279
				    return -1;
ali@449
   280
				}
ali@449
   281
			} else if (!filesystem)
ali@449
   282
				filesystem = buf.st_dev;
ali@444
   283
ali@444
   284
			if (buf.st_dev != filesystem || access(abspath, W_OK)) {
ali@449
   285
				if (can_step_back) {
ali@449
   286
					if (s == abspath)
ali@449
   287
						s[1] = saved;
ali@449
   288
					else
ali@449
   289
						s[0] = '/';
ali@449
   290
				}
ali@444
   291
				len = strlen(abspath);
ali@444
   292
				break;
ali@449
   293
			} else
ali@449
   294
				can_step_back = 1;
ali@444
   295
		}
ali@444
   296
ali@444
   297
		if (len == 1)
ali@444
   298
			len = 0;	/* Avoid an unslightly double slash. */
ali@444
   299
		atomic->toplevel = malloc(len + strlen("/.atomic-XXXXXX") + 1);
ali@444
   300
		memcpy(atomic->toplevel, abspath, len);
ali@444
   301
		strcpy(atomic->toplevel + len, "/.atomic-XXXXXX");
ali@444
   302
ali@444
   303
		free(abspath);
ali@444
   304
	}
ali@435
   305
#endif
ali@435
   306
ali@435
   307
	if (!mkdtemp(atomic->toplevel)) {
ali@435
   308
		int err = errno;
ali@435
   309
ali@435
   310
#ifdef EACCES
ali@435
   311
		if (err == EACCES) {
ali@435
   312
			char *s = strdup("atomic-XXXXXX");
ali@435
   313
ali@444
   314
#ifndef MSWIN_API
ali@444
   315
			if (stat(".", &buf) < 0) {
ali@447
   316
				atomic->error = razor_error_new_posix(".");
ali@444
   317
				free(s);
ali@444
   318
				free(atomic->toplevel);
ali@444
   319
				atomic->toplevel = NULL;
ali@444
   320
				return -1;
ali@444
   321
			}
ali@444
   322
			if (buf.st_dev != filesystem)
ali@444
   323
				/*
ali@444
   324
				 * Don't use a different filesystem. It will
ali@444
   325
				 * only fail later on (in rename) and cause
ali@444
   326
				 * an unhelpful error message (EXDEV).
ali@444
   327
				 */
ali@444
   328
				free(s);
ali@444
   329
			else
ali@444
   330
#endif
ali@435
   331
			if (mkdtemp(s)) {
ali@435
   332
				free(atomic->toplevel);
ali@435
   333
				atomic->toplevel = s;
ali@435
   334
				return 0;
ali@435
   335
			} else
ali@435
   336
				free(s);
ali@435
   337
		}
ali@435
   338
#endif
ali@435
   339
ali@447
   340
		atomic->error = razor_error_new_str(RAZOR_POSIX_ERROR, err,
ali@447
   341
						    atomic->toplevel,
ali@435
   342
						    strerror(err));
ali@435
   343
ali@435
   344
		free(atomic->toplevel);
ali@435
   345
		atomic->toplevel = NULL;
ali@435
   346
	}
ali@435
   347
ali@435
   348
	return !atomic->toplevel;
ali@435
   349
}
ali@435
   350
ali@416
   351
RAZOR_EXPORT int
ali@416
   352
razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
ali@416
   353
		       const char *path)
ali@416
   354
{
ali@416
   355
	struct atomic_action *a;
ali@416
   356
ali@435
   357
	razor_atomic_set_toplevel_from_path(atomic, *root ? root : path);
ali@435
   358
ali@416
   359
	if (razor_atomic_in_error_state(atomic))
ali@416
   360
		return -1;
ali@416
   361
ali@416
   362
	a = atomic_action_new(ACTION_MAKE_DIRS);
ali@416
   363
	a->args.path = strdup(path);
ali@416
   364
	a->args.u.make_dirs.root = strdup(root);
ali@416
   365
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   366
ali@416
   367
	return 0;
ali@416
   368
}
ali@416
   369
ali@416
   370
RAZOR_EXPORT int
ali@416
   371
razor_atomic_remove(struct razor_atomic *atomic, const char *path)
ali@416
   372
{
ali@416
   373
	struct atomic_action *a;
ali@416
   374
ali@435
   375
	razor_atomic_set_toplevel_from_path(atomic, path);
ali@435
   376
ali@416
   377
	if (razor_atomic_in_error_state(atomic))
ali@416
   378
		return -1;
ali@416
   379
ali@416
   380
	a = atomic_action_new(ACTION_REMOVE);
ali@416
   381
	a->args.path = strdup(path);
ali@416
   382
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   383
ali@416
   384
	return 0;
ali@416
   385
}
ali@416
   386
ali@416
   387
RAZOR_EXPORT int
ali@416
   388
razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
ali@416
   389
			 const char *newpath)
ali@416
   390
{
ali@416
   391
	struct atomic_action *a;
ali@416
   392
ali@435
   393
	razor_atomic_set_toplevel_from_path(atomic, newpath);
ali@435
   394
ali@416
   395
	if (razor_atomic_in_error_state(atomic))
ali@416
   396
		return -1;
ali@416
   397
ali@416
   398
	a = atomic_action_new(ACTION_MOVE);
ali@416
   399
	a->args.path = strdup(oldpath);
ali@416
   400
	a->args.u.move.dest = strdup(newpath);
ali@416
   401
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   402
ali@416
   403
	return 0;
ali@416
   404
}
ali@416
   405
ali@416
   406
RAZOR_EXPORT int
ali@416
   407
razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
ali@416
   408
			mode_t mode)
ali@416
   409
{
ali@416
   410
	struct atomic_action *a;
ali@416
   411
ali@435
   412
	razor_atomic_set_toplevel_from_path(atomic, dirname);
ali@435
   413
ali@416
   414
	if (razor_atomic_in_error_state(atomic))
ali@416
   415
		return -1;
ali@416
   416
ali@416
   417
	a = atomic_action_new(ACTION_CREATE_DIR);
ali@416
   418
	a->args.path = strdup(dirname);
ali@416
   419
	a->args.u.create_dir.mode = mode;
ali@416
   420
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   421
ali@416
   422
	return 0;
ali@416
   423
}
ali@416
   424
ali@416
   425
RAZOR_EXPORT int
ali@416
   426
razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
ali@416
   427
			    const char *path)
ali@416
   428
{
ali@416
   429
#if HAVE_SYMLINK
ali@416
   430
	struct atomic_action *a;
ali@435
   431
ali@435
   432
	razor_atomic_set_toplevel_from_path(atomic, path);
ali@416
   433
#endif
ali@416
   434
ali@416
   435
	if (razor_atomic_in_error_state(atomic))
ali@416
   436
		return -1;
ali@416
   437
ali@416
   438
#if HAVE_SYMLINK
ali@416
   439
	a = atomic_action_new(ACTION_CREATE_SYMLINK);
ali@416
   440
	a->args.path = strdup(path);
ali@416
   441
	a->args.u.create_symlink.target = strdup(target);
ali@416
   442
	atomic->actions = atomic_action_list_prepend(atomic->actions, a);
ali@416
   443
ali@416
   444
	return 0;
ali@416
   445
#else
ali@447
   446
	atomic->error = razor_error_new_str(RAZOR_POSIX_ERROR, ENOSYS, NULL,
ali@423
   447
					    "Symbolic links not supported "
ali@423
   448
					    "on this platform");
ali@416
   449
ali@416
   450
	return -1;
ali@416
   451
#endif
ali@416
   452
}
ali@416
   453
ali@416
   454
RAZOR_EXPORT int
ali@416
   455
razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
ali@416
   456
                         mode_t mode)
ali@416
   457
{
ali@416
   458
	int fd;
ali@416
   459
	struct atomic_action *a;
ali@416
   460
	char *tmpnam;
ali@416
   461
ali@435
   462
	razor_atomic_set_toplevel_from_path(atomic, filename);
ali@435
   463
ali@416
   464
	if (razor_atomic_in_error_state(atomic))
ali@416
   465
		return -1;
ali@416
   466
ali@416
   467
	tmpnam = atomic_action_attic_tmpnam(atomic);
ali@416
   468
	fd = open(tmpnam, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
ali@416
   469
		  mode & (S_IRWXU | S_IRWXG | S_IRWXO));
ali@416
   470
ali@416
   471
	if (fd == -1)
ali@447
   472
		atomic->error = razor_error_new_posix(filename);
ali@416
   473
	else {
ali@416
   474
		a = atomic_action_new(ACTION_MOVE);
ali@416
   475
		a->args.path = tmpnam;
ali@416
   476
		a->args.u.move.dest = strdup(filename);
ali@416
   477
		atomic->actions = atomic_action_list_prepend(atomic->actions,
ali@416
   478
							     a);
ali@416
   479
	}
ali@416
   480
ali@416
   481
	return fd;
ali@416
   482
}
ali@416
   483
ali@416
   484
#endif	/* ENABLE_ATOMIC && !HAVE_WINDOWS_KTM */