librazor/atomic.c
author J. Ali Harlow <ali@juiblex.co.uk>
Wed Feb 01 12:49:13 2012 +0000 (2012-02-01)
changeset 412 810d9ba06afd
child 416 d0aa9e0a6d04
permissions -rw-r--r--
Fix bug causing scripts to be run at the wrong time
ali@403
     1
/*
ali@403
     2
 * Copyright (C) 2011  J. Ali Harlow <ali@juiblex.co.uk>
ali@403
     3
 *
ali@403
     4
 * This program is free software; you can redistribute it and/or modify
ali@403
     5
 * it under the terms of the GNU General Public License as published by
ali@403
     6
 * the Free Software Foundation; either version 2 of the License, or
ali@403
     7
 * (at your option) any later version.
ali@403
     8
 *
ali@403
     9
 * This program is distributed in the hope that it will be useful,
ali@403
    10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
ali@403
    11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
ali@403
    12
 * GNU General Public License for more details.
ali@403
    13
 *
ali@403
    14
 * You should have received a copy of the GNU General Public License along
ali@403
    15
 * with this program; if not, write to the Free Software Foundation, Inc.,
ali@403
    16
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
ali@403
    17
 */
ali@403
    18
ali@403
    19
#include "config.h"
ali@403
    20
ali@403
    21
#include <stdlib.h>
ali@403
    22
#ifdef MSWIN_API
ali@403
    23
#include <windows.h>
ali@403
    24
#endif
ali@403
    25
#include <stdio.h>
ali@403
    26
#include <limits.h>
ali@403
    27
#include <errno.h>
ali@403
    28
#include <unistd.h>
ali@403
    29
#include <fcntl.h>
ali@403
    30
#include <sys/stat.h>
ali@403
    31
#include <string.h>
ali@403
    32
#include <assert.h>
ali@403
    33
#if HAVE_WINDOWS_KTM
ali@403
    34
#include <wchar.h>
ali@403
    35
#include <ktmw32.h>
ali@403
    36
#endif
ali@403
    37
ali@403
    38
#include "razor.h"
ali@403
    39
#include "razor-internal.h"
ali@403
    40
ali@403
    41
/*
ali@403
    42
 * Atomic transactions
ali@403
    43
 */
ali@403
    44
ali@403
    45
#ifndef O_BINARY
ali@403
    46
#define O_BINARY	0
ali@403
    47
#endif
ali@403
    48
ali@403
    49
#define RAZOR_ASCII_ISALPHA(c)	\
ali@403
    50
			((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z')
ali@403
    51
ali@403
    52
static int allow_all_root_names = 0;
ali@403
    53
ali@403
    54
/*
ali@403
    55
 * Primarily intended for testing named roots under UNIX platforms.
ali@403
    56
 */
ali@403
    57
RAZOR_EXPORT void
ali@403
    58
razor_disable_root_name_checks(int disable)
ali@403
    59
{
ali@403
    60
	allow_all_root_names = disable;
ali@403
    61
}
ali@403
    62
ali@403
    63
#ifdef MSWIN_API
ali@403
    64
ali@403
    65
static char *
ali@403
    66
razor_utf16_to_utf8(const wchar_t *utf16, int len)
ali@403
    67
{
ali@403
    68
	int n;
ali@403
    69
	char *utf8;
ali@403
    70
ali@403
    71
	n = WideCharToMultiByte(CP_UTF8, 0, utf16, len, NULL, 0, NULL, NULL);
ali@403
    72
	if (len >= 0 && utf16[len])
ali@403
    73
		n++;
ali@403
    74
	utf8 = malloc(n);
ali@403
    75
	(void)WideCharToMultiByte(CP_UTF8, 0, utf16, len, utf8, n, NULL, NULL);
ali@403
    76
	if (len >= 0 && utf16[len])
ali@403
    77
		utf8[n - 1] = 0;
ali@403
    78
ali@403
    79
	return utf8;
ali@403
    80
}
ali@403
    81
ali@403
    82
static wchar_t *
ali@403
    83
razor_utf8_to_utf16(const char *utf8, int len)
ali@403
    84
{
ali@403
    85
	int n;
ali@403
    86
	wchar_t *utf16;
ali@403
    87
ali@403
    88
	n = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0);
ali@403
    89
	if (len >= 0 && utf8[len])
ali@403
    90
		n++;
ali@403
    91
	utf16 = malloc(n * sizeof(wchar_t));
ali@403
    92
	(void)MultiByteToWideChar(CP_UTF8, 0, utf8, len, utf16, n);
ali@403
    93
	if (len >= 0 && utf8[len])
ali@403
    94
		utf16[n - 1] = 0;
ali@403
    95
ali@403
    96
	return utf16;
ali@403
    97
}
ali@403
    98
ali@403
    99
#endif	/* MSWIN_API */
ali@403
   100
ali@403
   101
#if HAVE_WINDOWS_KTM
ali@403
   102
ali@403
   103
static int
ali@403
   104
razor_valid_root_name(const wchar_t *name)
ali@403
   105
{
ali@403
   106
	if (allow_all_root_names)
ali@403
   107
		return !wcschr(name, '/');
ali@403
   108
ali@403
   109
	return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
ali@403
   110
	       name[2] == '\0';
ali@403
   111
}
ali@403
   112
ali@403
   113
struct razor_atomic {
ali@403
   114
	HANDLE transaction;
ali@403
   115
	int n_files;
ali@403
   116
	struct razor_atomic_file {
ali@403
   117
		wchar_t *path;
ali@403
   118
		HANDLE h;
ali@403
   119
	} *files;
ali@403
   120
	char *error_path;
ali@403
   121
	char *error_str;
ali@403
   122
	char *error_msg;
ali@403
   123
};
ali@403
   124
ali@403
   125
struct razor_wstr {
ali@403
   126
	wchar_t *str;
ali@403
   127
	int len, allocated;
ali@403
   128
};
ali@403
   129
ali@403
   130
static struct razor_wstr *
ali@403
   131
razor_wstr_create(const char *init, int len)
ali@403
   132
{
ali@403
   133
	int n;
ali@403
   134
	struct razor_wstr *wstr;
ali@403
   135
ali@403
   136
	wstr = malloc(sizeof(struct razor_wstr));
ali@403
   137
ali@403
   138
	n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0);
ali@403
   139
	if (len >= 0 && init[len])
ali@403
   140
		wstr->len = n++;
ali@403
   141
	else
ali@403
   142
		wstr->len = n - 1;
ali@403
   143
ali@403
   144
	wstr->allocated = n * 2;
ali@403
   145
	wstr->str = malloc(wstr->allocated * sizeof(wchar_t));
ali@403
   146
	if (!wstr->str) {
ali@403
   147
		free(wstr);
ali@403
   148
		return NULL;
ali@403
   149
	}
ali@403
   150
ali@403
   151
	(void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n);
ali@403
   152
	if (len >= 0 && init[len])
ali@403
   153
		wstr->str[wstr->len] = 0;
ali@403
   154
ali@403
   155
	return wstr;
ali@403
   156
}
ali@403
   157
ali@403
   158
static int
ali@403
   159
razor_wstr_append(struct razor_wstr *wstr, const char *s, int len)
ali@403
   160
{
ali@403
   161
	int n, allocated;
ali@403
   162
	wchar_t *str;
ali@403
   163
ali@403
   164
	n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0);
ali@403
   165
	if (len < 0 || !s[len])
ali@403
   166
		n--;
ali@403
   167
ali@403
   168
	if (wstr->allocated <= wstr->len + n) {
ali@403
   169
		allocated = (wstr->len + n + 1) * 2;
ali@403
   170
		str = realloc(wstr->str, allocated * sizeof(wchar_t));
ali@403
   171
		if (!str)
ali@403
   172
			return -1;
ali@403
   173
		wstr->allocated = allocated;
ali@403
   174
		wstr->str = str;
ali@403
   175
	}
ali@403
   176
ali@403
   177
	(void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n);
ali@403
   178
	wstr->len += n;
ali@403
   179
	wstr->str[wstr->len] = 0;
ali@403
   180
ali@403
   181
	return 0;
ali@403
   182
}
ali@403
   183
ali@403
   184
static void
ali@403
   185
razor_wstr_destroy(struct razor_wstr *wstr)
ali@403
   186
{
ali@403
   187
	free(wstr->str);
ali@403
   188
	free(wstr);
ali@403
   189
}
ali@403
   190
ali@403
   191
RAZOR_EXPORT struct razor_atomic *
ali@403
   192
razor_atomic_open(const char *description)
ali@403
   193
{
ali@403
   194
	wchar_t *buf;
ali@403
   195
	struct razor_atomic *atomic;
ali@403
   196
ali@403
   197
	atomic = zalloc(sizeof *atomic);
ali@403
   198
	buf = razor_utf8_to_utf16(description, -1);
ali@403
   199
	atomic->transaction = CreateTransaction(NULL, 0,
ali@403
   200
						TRANSACTION_DO_NOT_PROMOTE,
ali@403
   201
						0, 0, 0, buf);
ali@403
   202
	free(buf);
ali@403
   203
ali@403
   204
	return atomic;
ali@403
   205
}
ali@403
   206
ali@403
   207
static void
ali@403
   208
razor_atomic_set_error_str(struct razor_atomic *atomic, const wchar_t *path,
ali@403
   209
			   const char *str)
ali@403
   210
{
ali@403
   211
	assert(!atomic->error_str);
ali@403
   212
ali@403
   213
	free(atomic->error_path);
ali@403
   214
ali@403
   215
	if (path)
ali@403
   216
		atomic->error_path = razor_utf16_to_utf8(path, -1);
ali@403
   217
	else
ali@403
   218
		atomic->error_path = NULL;
ali@403
   219
ali@403
   220
	atomic->error_str = strdup(str);
ali@403
   221
}
ali@403
   222
ali@403
   223
static void
ali@403
   224
razor_atomic_set_error(struct razor_atomic *atomic, const wchar_t *path,
ali@403
   225
		       DWORD error)
ali@403
   226
{
ali@403
   227
	wchar_t *buf;
ali@403
   228
ali@403
   229
	assert(!atomic->error_str);
ali@403
   230
ali@403
   231
	free(atomic->error_path);
ali@403
   232
ali@403
   233
	if (path)
ali@403
   234
		atomic->error_path = razor_utf16_to_utf8(path, -1);
ali@403
   235
	else
ali@403
   236
		atomic->error_path = NULL;
ali@403
   237
ali@403
   238
	FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|
ali@403
   239
		       FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
ali@403
   240
		       NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
ali@403
   241
		       (LPWSTR)&buf, 0, NULL);
ali@403
   242
	atomic->error_str = razor_utf16_to_utf8(buf, -1);
ali@403
   243
	LocalFree(buf);
ali@403
   244
}
ali@403
   245
ali@403
   246
RAZOR_EXPORT int
ali@403
   247
razor_atomic_commit(struct razor_atomic *atomic)
ali@403
   248
{
ali@403
   249
	int retval;
ali@403
   250
ali@403
   251
	if (atomic->error_str)
ali@403
   252
		return -1;
ali@403
   253
ali@403
   254
	retval = !CommitTransaction(atomic->transaction);
ali@403
   255
ali@403
   256
	if (retval) {
ali@403
   257
		razor_atomic_set_error(atomic, NULL, GetLastError());
ali@403
   258
		RollbackTransaction(atomic->transaction);
ali@403
   259
	}
ali@403
   260
ali@403
   261
	CloseHandle(atomic->transaction);
ali@403
   262
	atomic->transaction = INVALID_HANDLE_VALUE;
ali@403
   263
ali@403
   264
	return retval;
ali@403
   265
}
ali@403
   266
ali@403
   267
RAZOR_EXPORT void
ali@403
   268
razor_atomic_destroy(struct razor_atomic *atomic)
ali@403
   269
{
ali@403
   270
	int i;
ali@403
   271
ali@403
   272
	for(i = 0; i < atomic->n_files; i++) {
ali@403
   273
		if (atomic->files[i].h != INVALID_HANDLE_VALUE) {
ali@403
   274
			CloseHandle(atomic->files[i].h);
ali@403
   275
			free(atomic->files[i].path);
ali@403
   276
		}
ali@403
   277
	}
ali@403
   278
	free(atomic->files);
ali@403
   279
	if (atomic->transaction != INVALID_HANDLE_VALUE) {
ali@403
   280
		RollbackTransaction(atomic->transaction);
ali@403
   281
		CloseHandle(atomic->transaction);
ali@403
   282
	}
ali@403
   283
	free(atomic->error_path);
ali@403
   284
	free(atomic->error_str);
ali@403
   285
	free(atomic->error_msg);
ali@403
   286
	free(atomic);
ali@403
   287
}
ali@403
   288
ali@403
   289
RAZOR_EXPORT int
ali@403
   290
razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
ali@403
   291
		       const char *path)
ali@403
   292
{
ali@403
   293
	struct razor_wstr *buffer;
ali@403
   294
	const char *slash, *s, *next;
ali@403
   295
	WIN32_FILE_ATTRIBUTE_DATA fa;
ali@403
   296
	DWORD err;
ali@403
   297
	int r, creating = 0;
ali@403
   298
ali@403
   299
	if (atomic->error_str)
ali@403
   300
		return -1;
ali@403
   301
ali@403
   302
	buffer = razor_wstr_create(root, -1);
ali@403
   303
	slash = path;
ali@403
   304
ali@403
   305
	for (; *slash != '\0'; slash = next) {
ali@403
   306
		next = strpbrk(slash + 1, "/\\");
ali@403
   307
		if (next == NULL)
ali@403
   308
			break;
ali@403
   309
ali@403
   310
		razor_wstr_append(buffer, slash, next - slash);
ali@403
   311
ali@403
   312
		if (!creating) {
ali@403
   313
			if (razor_valid_root_name(buffer->str))
ali@403
   314
				continue;
ali@403
   315
ali@403
   316
			r = GetFileAttributesTransactedW(buffer->str,
ali@403
   317
							 GetFileExInfoStandard,
ali@403
   318
							 &fa,
ali@403
   319
							 atomic->transaction);
ali@403
   320
ali@403
   321
			if (!r) {
ali@403
   322
				err = GetLastError();
ali@403
   323
				if (err == ERROR_FILE_NOT_FOUND) {
ali@403
   324
					creating = 1;
ali@403
   325
				} else {
ali@403
   326
					razor_atomic_set_error(atomic,
ali@403
   327
							       buffer->str,
ali@403
   328
							       err);
ali@403
   329
					razor_wstr_destroy(buffer);
ali@403
   330
					return -1;
ali@403
   331
				}
ali@403
   332
			} else if (!(fa.dwFileAttributes&
ali@403
   333
				     FILE_ATTRIBUTE_DIRECTORY)) {
ali@403
   334
				razor_atomic_set_error_str(atomic, buffer->str,
ali@403
   335
							   "Not a directory");
ali@403
   336
				razor_wstr_destroy(buffer);
ali@403
   337
				return -1;
ali@403
   338
			}
ali@403
   339
		}
ali@403
   340
		if (creating) {
ali@403
   341
			if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL,
ali@403
   342
							atomic->transaction)) {
ali@403
   343
				razor_atomic_set_error(atomic, buffer->str,
ali@403
   344
						       GetLastError());
ali@403
   345
				razor_wstr_destroy(buffer);
ali@403
   346
				return -1;
ali@403
   347
			}
ali@403
   348
ali@403
   349
			/* FIXME: What to do about permissions for dirs we
ali@403
   350
			 * have to create but are not in the cpio archive? */
ali@403
   351
		}
ali@403
   352
	}
ali@403
   353
ali@403
   354
	razor_wstr_destroy(buffer);
ali@403
   355
ali@403
   356
	return 0;
ali@403
   357
}
ali@403
   358
ali@403
   359
RAZOR_EXPORT int
ali@403
   360
razor_atomic_remove(struct razor_atomic *atomic, const char *path)
ali@403
   361
{
ali@403
   362
	wchar_t *buf;
ali@403
   363
	DWORD err;
ali@403
   364
ali@403
   365
	if (atomic->error_str)
ali@403
   366
		return -1;
ali@403
   367
ali@403
   368
	buf = razor_utf8_to_utf16(path, -1);
ali@403
   369
ali@403
   370
	if (DeleteFileTransactedW(buf, atomic->transaction)) {
ali@403
   371
		free(buf);
ali@403
   372
		return 0;
ali@403
   373
	}
ali@403
   374
ali@403
   375
	err = GetLastError();
ali@403
   376
	if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
ali@403
   377
		free(buf);
ali@403
   378
		return 0;
ali@403
   379
	}
ali@403
   380
ali@403
   381
	if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL,
ali@403
   382
					 atomic->transaction)) {
ali@403
   383
		if (DeleteFileTransactedW(buf, atomic->transaction)) {
ali@403
   384
			free(buf);
ali@403
   385
			return 0;
ali@403
   386
		}
ali@403
   387
		err = GetLastError();
ali@403
   388
	}
ali@403
   389
ali@403
   390
	if (RemoveDirectoryTransactedW(buf, atomic->transaction) ||
ali@403
   391
	    GetLastError() == ERROR_DIR_NOT_EMPTY) {
ali@403
   392
		free(buf);
ali@403
   393
		return 0;
ali@403
   394
	}
ali@403
   395
ali@403
   396
	/*
ali@403
   397
	 * It would be tempting to use:
ali@403
   398
	 * 	MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)
ali@403
   399
	 * but unless we can guarantee that the system will be rebooted
ali@403
   400
	 * before we (or some other application) write another file with the
ali@403
   401
	 * same path, this is likely to cause more problems than it solves.
ali@403
   402
	 */
ali@403
   403
ali@403
   404
	razor_atomic_set_error(atomic, buf, err);
ali@403
   405
	free(buf);
ali@403
   406
	return -1;
ali@403
   407
}
ali@403
   408
ali@403
   409
RAZOR_EXPORT int
ali@403
   410
razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
ali@403
   411
			 const char *newpath)
ali@403
   412
{
ali@403
   413
	wchar_t *oldbuf, *newbuf;
ali@403
   414
	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
ali@403
   415
ali@403
   416
	if (atomic->error_str)
ali@403
   417
		return -1;
ali@403
   418
ali@403
   419
	newbuf = razor_utf8_to_utf16(newpath, -1);
ali@403
   420
	oldbuf = razor_utf8_to_utf16(oldpath, -1);
ali@403
   421
ali@403
   422
	/*
ali@403
   423
	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will
ali@403
   424
	 * cover every case we care about _except_ replacing an empty
ali@403
   425
	 * directory with a file. Calling RemoveDirectoryTransacted() will deal
ali@403
   426
	 * with this case while having no effect in all other cases.
ali@403
   427
	 */
ali@403
   428
	(void)RemoveDirectoryTransactedW(newbuf, atomic->transaction);
ali@403
   429
ali@403
   430
	if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags,
ali@403
   431
			         atomic->transaction))
ali@403
   432
		razor_atomic_set_error(atomic, newbuf, GetLastError());
ali@403
   433
ali@403
   434
	free(newbuf);
ali@403
   435
	free(oldbuf);
ali@403
   436
ali@403
   437
	return !!atomic->error_str;
ali@403
   438
}
ali@403
   439
ali@403
   440
RAZOR_EXPORT int
ali@403
   441
razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
ali@403
   442
			mode_t mode)
ali@403
   443
{
ali@403
   444
	wchar_t *buf;
ali@403
   445
	DWORD err;
ali@403
   446
	WIN32_FILE_ATTRIBUTE_DATA fa;
ali@403
   447
ali@403
   448
	if (atomic->error_str)
ali@403
   449
		return -1;
ali@403
   450
ali@403
   451
	buf = razor_utf8_to_utf16(dirname, -1);
ali@403
   452
ali@403
   453
	if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) {
ali@403
   454
		err = GetLastError();
ali@403
   455
		if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) {
ali@403
   456
abort:
ali@403
   457
			razor_atomic_set_error(atomic, buf, err);
ali@403
   458
			free(buf);
ali@403
   459
			return -1;
ali@403
   460
		}
ali@403
   461
ali@403
   462
		if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard,
ali@403
   463
						  &fa, atomic->transaction))
ali@403
   464
			goto abort;
ali@403
   465
ali@403
   466
		if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) {
ali@403
   467
			if (razor_atomic_remove(atomic, dirname)) {
ali@403
   468
				free(buf);
ali@403
   469
				return -1;
ali@403
   470
			}
ali@403
   471
			if (!CreateDirectoryTransactedW(NULL, buf, NULL,
ali@403
   472
							atomic->transaction)) {
ali@403
   473
				err = GetLastError();
ali@403
   474
				goto abort;
ali@403
   475
			}
ali@403
   476
		}
ali@403
   477
	}
ali@403
   478
ali@403
   479
	free(buf);
ali@403
   480
ali@403
   481
	return 0;
ali@403
   482
}
ali@403
   483
ali@403
   484
RAZOR_EXPORT int
ali@403
   485
razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
ali@403
   486
			    const char *path)
ali@403
   487
{
ali@403
   488
	if (atomic->error_str)
ali@403
   489
		return -1;
ali@403
   490
ali@403
   491
	/*
ali@403
   492
	 * This isn't true, but symbolic links under Windows 7
ali@403
   493
	 * need to know whether the target is a directory or not
ali@403
   494
	 * and we don't always know that at the time when the
ali@403
   495
	 * link is created, so it's a convienent lie for now.
ali@403
   496
	 */
ali@403
   497
	razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported "
ali@403
   498
						 "on this platform");
ali@403
   499
ali@403
   500
	return -1;
ali@403
   501
}
ali@403
   502
ali@403
   503
RAZOR_EXPORT int
ali@403
   504
razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
ali@403
   505
			 mode_t mode)
ali@403
   506
{
ali@403
   507
	DWORD attribs;
ali@403
   508
	struct razor_atomic_file *files;
ali@403
   509
	int i = atomic->n_files;
ali@403
   510
ali@403
   511
	if (atomic->error_str)
ali@403
   512
		return -1;
ali@403
   513
ali@403
   514
	files = realloc(atomic->files,
ali@403
   515
			(atomic->n_files+1) * sizeof(struct razor_atomic_file));
ali@403
   516
	if (!files) {
ali@403
   517
		razor_atomic_set_error_str(atomic, NULL, "Not enough memory");
ali@403
   518
		return -1;
ali@403
   519
	}
ali@403
   520
	atomic->n_files++;
ali@403
   521
	atomic->files = files;
ali@403
   522
ali@403
   523
	files[i].path = razor_utf8_to_utf16(filename, -1);
ali@403
   524
ali@403
   525
	/*
ali@403
   526
	 * Passing CREATE_ALWAYS to CreateFileTransacted() will cover
ali@403
   527
	 * every case we care about _except_ replacing an empty directory
ali@403
   528
	 * with a file. Calling RemoveDirectoryTransacted() will deal
ali@403
   529
	 * with this case while having no effect in all other cases.
ali@403
   530
	 */
ali@403
   531
	(void)RemoveDirectoryTransactedW(files[i].path, atomic->transaction);
ali@403
   532
ali@403
   533
	if (mode & S_IWUSR)
ali@403
   534
		attribs = FILE_ATTRIBUTE_NORMAL;
ali@403
   535
	else
ali@403
   536
		attribs = FILE_ATTRIBUTE_READONLY;
ali@403
   537
ali@403
   538
	files[i].h = CreateFileTransactedW(files[i].path, GENERIC_WRITE,
ali@403
   539
					   0, NULL, CREATE_ALWAYS, attribs,
ali@403
   540
					   NULL, atomic->transaction, NULL,
ali@403
   541
					   NULL);
ali@403
   542
ali@403
   543
	if (files[i].h == INVALID_HANDLE_VALUE) {
ali@403
   544
		razor_atomic_set_error(atomic, files[i].path, GetLastError());
ali@403
   545
		free(files[i].path);
ali@403
   546
		atomic->n_files--;
ali@403
   547
		return -1;
ali@403
   548
	}
ali@403
   549
ali@403
   550
	return i;
ali@403
   551
}
ali@403
   552
ali@403
   553
RAZOR_EXPORT int
ali@403
   554
razor_atomic_write(struct razor_atomic *atomic, int handle, const void *data,
ali@403
   555
		   size_t size)
ali@403
   556
{
ali@403
   557
	DWORD written;
ali@403
   558
ali@403
   559
	if (atomic->error_str)
ali@403
   560
		return -1;
ali@403
   561
ali@403
   562
	assert(handle < atomic->n_files);
ali@403
   563
	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
ali@403
   564
ali@403
   565
	while(size) {
ali@403
   566
		if (!WriteFile(atomic->files[handle].h, data, size, &written,
ali@403
   567
			       NULL)) {
ali@403
   568
			razor_atomic_set_error(atomic,
ali@403
   569
					       atomic->files[handle].path,
ali@403
   570
					       GetLastError());
ali@403
   571
ali@403
   572
			(void)CloseHandle(atomic->files[handle].h);
ali@403
   573
			free(atomic->files[handle].path);
ali@403
   574
			atomic->files[handle].path = NULL;
ali@403
   575
			atomic->files[handle].h = INVALID_HANDLE_VALUE;
ali@403
   576
ali@403
   577
			return -1;
ali@403
   578
		}
ali@403
   579
ali@403
   580
		data += written;
ali@403
   581
		size -= written;
ali@403
   582
	}
ali@403
   583
ali@403
   584
	return 0;
ali@403
   585
}
ali@403
   586
ali@403
   587
RAZOR_EXPORT int
ali@403
   588
razor_atomic_sync(struct razor_atomic *atomic, int handle)
ali@403
   589
{
ali@403
   590
	HANDLE h;
ali@403
   591
ali@403
   592
	if (atomic->error_str)
ali@403
   593
		return -1;
ali@403
   594
ali@403
   595
	assert(handle < atomic->n_files);
ali@403
   596
	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
ali@403
   597
ali@403
   598
	if (!CloseHandle(atomic->files[handle].h)) {
ali@403
   599
		razor_atomic_set_error(atomic, atomic->files[handle].path,
ali@403
   600
				       GetLastError());
ali@403
   601
		free(atomic->files[handle].path);
ali@403
   602
		atomic->files[handle].path = NULL;
ali@403
   603
		atomic->files[handle].h = INVALID_HANDLE_VALUE;
ali@403
   604
		return -1;
ali@403
   605
	}
ali@403
   606
ali@403
   607
	h = CreateFileTransactedW(atomic->files[handle].path, GENERIC_WRITE, 0,
ali@403
   608
				  NULL, OPEN_EXISTING, 0, NULL,
ali@403
   609
				  atomic->transaction, NULL, NULL);
ali@403
   610
	atomic->files[handle].h = h;
ali@403
   611
ali@403
   612
	if (atomic->files[handle].h == INVALID_HANDLE_VALUE) {
ali@403
   613
		razor_atomic_set_error(atomic, atomic->files[handle].path,
ali@403
   614
				       GetLastError());
ali@403
   615
		free(atomic->files[handle].path);
ali@403
   616
		atomic->files[handle].path = NULL;
ali@403
   617
		return -1;
ali@403
   618
	}
ali@403
   619
ali@403
   620
	return !!atomic->error_str;
ali@403
   621
}
ali@403
   622
ali@403
   623
RAZOR_EXPORT int
ali@403
   624
razor_atomic_close(struct razor_atomic *atomic, int handle)
ali@403
   625
{
ali@403
   626
	if (atomic->error_str)
ali@403
   627
		return -1;
ali@403
   628
ali@403
   629
	assert(handle < atomic->n_files);
ali@403
   630
	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
ali@403
   631
ali@403
   632
	if (!CloseHandle(atomic->files[handle].h))
ali@403
   633
		razor_atomic_set_error(atomic, atomic->files[handle].path,
ali@403
   634
				       GetLastError());
ali@403
   635
ali@403
   636
	free(atomic->files[handle].path);
ali@403
   637
	atomic->files[handle].path = NULL;
ali@403
   638
	atomic->files[handle].h = INVALID_HANDLE_VALUE;
ali@403
   639
ali@403
   640
	while(atomic->n_files > 0 &&
ali@403
   641
	      atomic->files[atomic->n_files-1].h == INVALID_HANDLE_VALUE)
ali@403
   642
		atomic->n_files--;
ali@403
   643
ali@403
   644
	return !!atomic->error_str;
ali@403
   645
}
ali@403
   646
ali@403
   647
#else		/* HAVE_WINDOWS_KVM */
ali@403
   648
ali@403
   649
static int
ali@403
   650
razor_valid_root_name(const char *name)
ali@403
   651
{
ali@403
   652
	if (allow_all_root_names) {
ali@403
   653
#ifdef MSWIN_API
ali@403
   654
		return !strpbrk(name, "/\\");
ali@403
   655
#else
ali@403
   656
		return !strchr(name, '/');
ali@403
   657
#endif
ali@403
   658
	}
ali@403
   659
ali@403
   660
#ifdef MSWIN_API
ali@403
   661
	return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
ali@403
   662
	       name[2] == '\0';
ali@403
   663
#else
ali@403
   664
	return name[0] == '\0';
ali@403
   665
#endif
ali@403
   666
}
ali@403
   667
ali@403
   668
struct razor_atomic {
ali@403
   669
	char *error_path;
ali@403
   670
	char *error_str;
ali@403
   671
	char *error_msg;
ali@403
   672
};
ali@403
   673
ali@403
   674
RAZOR_EXPORT struct razor_atomic *
ali@403
   675
razor_atomic_open(const char *description)
ali@403
   676
{
ali@403
   677
	struct razor_atomic *atomic;
ali@403
   678
ali@403
   679
	atomic = zalloc(sizeof *atomic);
ali@403
   680
ali@403
   681
	return atomic;
ali@403
   682
}
ali@403
   683
ali@403
   684
static void
ali@403
   685
razor_atomic_set_error_str(struct razor_atomic *atomic, const char *path,
ali@403
   686
			   const char *str)
ali@403
   687
{
ali@403
   688
	assert(!atomic->error_str);
ali@403
   689
ali@403
   690
	atomic->error_path = path ? strdup(path) : NULL;
ali@403
   691
	atomic->error_str = strdup(str);
ali@403
   692
}
ali@403
   693
ali@403
   694
#ifdef MSWIN_API
ali@403
   695
static void
ali@403
   696
razor_atomic_set_error_mswin(struct razor_atomic *atomic, const wchar_t *path,
ali@403
   697
		       DWORD error)
ali@403
   698
{
ali@403
   699
	wchar_t *buf;
ali@403
   700
ali@403
   701
	assert(!atomic->error_str);
ali@403
   702
ali@403
   703
	free(atomic->error_path);
ali@403
   704
ali@403
   705
	if (path)
ali@403
   706
		atomic->error_path = razor_utf16_to_utf8(path, -1);
ali@403
   707
	else
ali@403
   708
		atomic->error_path = NULL;
ali@403
   709
ali@403
   710
	FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|
ali@403
   711
		       FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
ali@403
   712
		       NULL, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
ali@403
   713
		       (LPWSTR)&buf, 0, NULL);
ali@403
   714
	atomic->error_str = razor_utf16_to_utf8(buf, -1);
ali@403
   715
	LocalFree(buf);
ali@403
   716
}
ali@403
   717
#endif
ali@403
   718
ali@403
   719
RAZOR_EXPORT int
ali@403
   720
razor_atomic_commit(struct razor_atomic *atomic)
ali@403
   721
{
ali@403
   722
	return !!atomic->error_str;
ali@403
   723
}
ali@403
   724
ali@403
   725
RAZOR_EXPORT void
ali@403
   726
razor_atomic_destroy(struct razor_atomic *atomic)
ali@403
   727
{
ali@403
   728
	free(atomic->error_path);
ali@403
   729
	free(atomic->error_str);
ali@403
   730
	free(atomic->error_msg);
ali@403
   731
	free(atomic);
ali@403
   732
}
ali@403
   733
ali@403
   734
RAZOR_EXPORT int
ali@403
   735
razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
ali@403
   736
		       const char *path)
ali@403
   737
{
ali@403
   738
	char buffer[PATH_MAX], *p;
ali@403
   739
	const char *slash, *next;
ali@403
   740
	struct stat buf;
ali@403
   741
ali@403
   742
	if (atomic->error_str)
ali@403
   743
		return -1;
ali@403
   744
ali@403
   745
	strcpy(buffer, root);
ali@403
   746
	p = buffer + strlen(buffer);
ali@403
   747
	slash = path;
ali@403
   748
	for (slash = path; *slash != '\0'; slash = next) {
ali@403
   749
#ifdef MSWIN_API
ali@403
   750
		next = strpbrk(slash + 1, "/\\");
ali@403
   751
#else
ali@403
   752
		next = strchr(slash + 1, '/');
ali@403
   753
#endif
ali@403
   754
		if (next == NULL)
ali@403
   755
			break;
ali@403
   756
ali@403
   757
		memcpy(p, slash, next - slash);
ali@403
   758
		p += next - slash;
ali@403
   759
		*p = '\0';
ali@403
   760
ali@403
   761
		if (razor_valid_root_name(buffer))
ali@403
   762
			continue;
ali@403
   763
ali@403
   764
		if (stat(buffer, &buf) == 0) {
ali@403
   765
			if (!S_ISDIR(buf.st_mode)) {
ali@403
   766
				razor_atomic_set_error_str(atomic, buffer,
ali@403
   767
							   "Not a directory");
ali@403
   768
				return -1;
ali@403
   769
			}
ali@403
   770
		} else if (mkdir(buffer, 0777) < 0) {
ali@403
   771
			razor_atomic_set_error_str(atomic, buffer,
ali@403
   772
						   strerror(errno));
ali@403
   773
			return -1;
ali@403
   774
		}
ali@403
   775
	}
ali@403
   776
ali@403
   777
	return 0;
ali@403
   778
}
ali@403
   779
ali@403
   780
RAZOR_EXPORT int
ali@403
   781
razor_atomic_remove(struct razor_atomic *atomic, const char *path)
ali@403
   782
{
ali@403
   783
#ifdef MSWIN_API
ali@403
   784
	wchar_t *buf;
ali@403
   785
	DWORD err;
ali@403
   786
#endif
ali@403
   787
ali@403
   788
	if (atomic->error_str)
ali@403
   789
		return -1;
ali@403
   790
ali@403
   791
#ifdef MSWIN_API
ali@403
   792
	buf = razor_utf8_to_utf16(path, -1);
ali@403
   793
ali@403
   794
	if (!DeleteFileW(buf)) {
ali@403
   795
		err = GetLastError();
ali@403
   796
		if (err != ERROR_FILE_NOT_FOUND &&
ali@403
   797
		    err != ERROR_PATH_NOT_FOUND &&
ali@403
   798
		    !(SetFileAttributesW(buf, FILE_ATTRIBUTE_NORMAL) &&
ali@403
   799
		      DeleteFileW(buf)) &&
ali@403
   800
		    !RemoveDirectoryW(buf) &&
ali@403
   801
		    GetLastError() != ERROR_DIR_NOT_EMPTY)
ali@403
   802
			razor_atomic_set_error_mswin(atomic, buf, err);
ali@403
   803
	}
ali@403
   804
ali@403
   805
	free(buf);
ali@403
   806
#else
ali@403
   807
	if (remove(path))
ali@403
   808
		razor_atomic_set_error_str(atomic, path, strerror(errno));
ali@403
   809
#endif
ali@403
   810
ali@403
   811
	return !!atomic->error_str;
ali@403
   812
}
ali@403
   813
ali@403
   814
RAZOR_EXPORT int
ali@403
   815
razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
ali@403
   816
			 const char *newpath)
ali@403
   817
{
ali@403
   818
#ifdef MSWIN_API
ali@403
   819
	wchar_t *oldbuf, *newbuf;
ali@403
   820
	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
ali@403
   821
#endif
ali@403
   822
ali@403
   823
	if (atomic->error_str)
ali@403
   824
		return -1;
ali@403
   825
ali@403
   826
#ifdef MSWIN_API
ali@403
   827
	newbuf = razor_utf8_to_utf16(newpath, -1);
ali@403
   828
	oldbuf = razor_utf8_to_utf16(oldpath, -1);
ali@403
   829
ali@403
   830
	/*
ali@403
   831
	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will
ali@403
   832
	 * cover every case we care about _except_ replacing an empty
ali@403
   833
	 * directory with a file. Calling RemoveDirectory() will deal
ali@403
   834
	 * with this case while having no effect in all other cases.
ali@403
   835
	 */
ali@403
   836
	(void)RemoveDirectoryW(newbuf);
ali@403
   837
ali@403
   838
	if (!MoveFileExW(oldbuf, newbuf, flags))
ali@403
   839
		razor_atomic_set_error_mswin(atomic, newbuf, GetLastError());
ali@403
   840
ali@403
   841
	free(newbuf);
ali@403
   842
	free(oldbuf);
ali@403
   843
#else
ali@403
   844
	if (rename(oldpath, newpath))
ali@403
   845
		razor_atomic_set_error_str(atomic, newpath, strerror(errno));
ali@403
   846
#endif
ali@403
   847
ali@403
   848
	return !!atomic->error_str;
ali@403
   849
}
ali@403
   850
ali@403
   851
RAZOR_EXPORT int
ali@403
   852
razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
ali@403
   853
			mode_t mode)
ali@403
   854
{
ali@403
   855
	if (atomic->error_str)
ali@403
   856
		return -1;
ali@403
   857
ali@403
   858
	if (!mkdir(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)))
ali@403
   859
		return 0;
ali@403
   860
ali@403
   861
	if (errno != EEXIST) {
ali@403
   862
		razor_atomic_set_error_str(atomic, dirname, strerror(errno));
ali@403
   863
		return -1;
ali@403
   864
	}
ali@403
   865
ali@403
   866
	if (chmod(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)) < 0) {
ali@403
   867
		razor_atomic_set_error_str(atomic, dirname, strerror(errno));
ali@403
   868
		return -1;
ali@403
   869
	}
ali@403
   870
ali@403
   871
	return 0;
ali@403
   872
}
ali@403
   873
ali@403
   874
RAZOR_EXPORT int
ali@403
   875
razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
ali@403
   876
			    const char *path)
ali@403
   877
{
ali@403
   878
	if (atomic->error_str)
ali@403
   879
		return -1;
ali@403
   880
ali@403
   881
#if HAVE_SYMLINK
ali@403
   882
	if (symlink(target, path) < 0) {
ali@403
   883
		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
ali@403
   884
		return -1;
ali@403
   885
	}
ali@403
   886
#else
ali@403
   887
	razor_atomic_set_error_str(atomic, NULL, "Symbolic links not supported "
ali@403
   888
						 "on this platform");
ali@403
   889
#endif
ali@403
   890
ali@403
   891
	return 0;
ali@403
   892
}
ali@403
   893
ali@403
   894
RAZOR_EXPORT int
ali@403
   895
razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
ali@403
   896
			 mode_t mode)
ali@403
   897
{
ali@403
   898
	int fd;
ali@403
   899
ali@403
   900
	if (atomic->error_str)
ali@403
   901
		return -1;
ali@403
   902
ali@403
   903
	atomic->error_path = strdup(filename);
ali@403
   904
	fd = open(atomic->error_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
ali@403
   905
		  mode & (S_IRWXU | S_IRWXG | S_IRWXO));
ali@403
   906
ali@403
   907
	if (fd == -1)
ali@403
   908
		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
ali@403
   909
ali@403
   910
	return fd;
ali@403
   911
}
ali@403
   912
ali@403
   913
RAZOR_EXPORT int
ali@403
   914
razor_atomic_write(struct razor_atomic *atomic, int fd, const void *data,
ali@403
   915
		   size_t size)
ali@403
   916
{
ali@403
   917
	int written;
ali@403
   918
ali@403
   919
	if (atomic->error_str)
ali@403
   920
		return -1;
ali@403
   921
ali@403
   922
	while(size) {
ali@403
   923
		written = write(fd, data, size);
ali@403
   924
		if (written < 0) {
ali@403
   925
			razor_atomic_set_error_str(atomic, NULL, strerror(errno));
ali@403
   926
ali@403
   927
			(void)close(fd);
ali@403
   928
ali@403
   929
			return -1;
ali@403
   930
		}
ali@403
   931
ali@403
   932
		data += written;
ali@403
   933
		size -= written;
ali@403
   934
	}
ali@403
   935
ali@403
   936
	return 0;
ali@403
   937
}
ali@403
   938
ali@403
   939
RAZOR_EXPORT int
ali@403
   940
razor_atomic_sync(struct razor_atomic *atomic, int handle)
ali@403
   941
{
ali@403
   942
	if (atomic->error_str)
ali@403
   943
		return -1;
ali@403
   944
ali@403
   945
	if (fsync(handle) < 0) {
ali@403
   946
		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
ali@403
   947
		return -1;
ali@403
   948
	}
ali@403
   949
ali@403
   950
	free(atomic->error_path);
ali@403
   951
	atomic->error_path = NULL;
ali@403
   952
ali@403
   953
	return 0;
ali@403
   954
}
ali@403
   955
ali@403
   956
RAZOR_EXPORT int
ali@403
   957
razor_atomic_close(struct razor_atomic *atomic, int fd)
ali@403
   958
{
ali@403
   959
	if (atomic->error_str)
ali@403
   960
		return -1;
ali@403
   961
ali@403
   962
	if (close(fd) < 0) {
ali@403
   963
		razor_atomic_set_error_str(atomic, NULL, strerror(errno));
ali@403
   964
		return -1;
ali@403
   965
	}
ali@403
   966
ali@403
   967
	free(atomic->error_path);
ali@403
   968
	atomic->error_path = NULL;
ali@403
   969
ali@403
   970
	return 0;
ali@403
   971
}
ali@403
   972
ali@403
   973
#endif		/* HAVE_WINDOWS_KVM */
ali@403
   974
ali@403
   975
RAZOR_EXPORT const char *
ali@403
   976
razor_atomic_get_error_msg(struct razor_atomic *atomic)
ali@403
   977
{
ali@403
   978
	if (!atomic->error_msg) {
ali@403
   979
		if (atomic->error_path)
ali@403
   980
			atomic->error_msg = razor_concat(atomic->error_path,
ali@403
   981
							 ": ",
ali@403
   982
							 atomic->error_str,
ali@403
   983
							 NULL);
ali@403
   984
		else
ali@403
   985
			atomic->error_msg = strdup(atomic->error_str);
ali@403
   986
	}
ali@403
   987
ali@403
   988
	return atomic->error_msg;
ali@403
   989
}
ali@403
   990
ali@403
   991
RAZOR_EXPORT void
ali@403
   992
razor_atomic_abort(struct razor_atomic *atomic, const char *error_msg)
ali@403
   993
{
ali@403
   994
	if (!atomic->error_str)
ali@403
   995
		razor_atomic_set_error_str(atomic, NULL, error_msg);
ali@403
   996
}
ali@403
   997
ali@403
   998
RAZOR_EXPORT int
ali@403
   999
razor_atomic_in_error_state(struct razor_atomic *atomic)
ali@403
  1000
{
ali@403
  1001
	return !!atomic->error_str;
ali@403
  1002
}