librazor/atomic-ktm.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Jun 07 18:36:20 2018 +0100 (2018-06-07)
changeset 499 c89e5edb8eae
parent 494 889dc38157ac
permissions -rw-r--r--
Improve error on failure to lock database
ali@416
     1
/*
ali@441
     2
 * Copyright (C) 2011, 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 HAVE_WINDOWS_KTM
ali@416
    22
ali@416
    23
#include <stdlib.h>
ali@416
    24
#include <windows.h>
ali@416
    25
#include <stdio.h>
ali@416
    26
#include <limits.h>
ali@475
    27
#include <unistd.h>
ali@416
    28
#include <errno.h>
ali@416
    29
#include <string.h>
ali@416
    30
#include <assert.h>
ali@416
    31
#include <wchar.h>
ali@416
    32
#include <ktmw32.h>
ali@416
    33
ali@416
    34
#include "razor.h"
ali@416
    35
#include "razor-internal.h"
ali@416
    36
ali@416
    37
static int
ali@416
    38
razor_valid_root_name2(const wchar_t *name)
ali@416
    39
{
ali@416
    40
	if (razor_allow_all_root_names())
ali@416
    41
		return !wcschr(name, '/');
ali@416
    42
ali@416
    43
	return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
ali@416
    44
	       name[2] == '\0';
ali@416
    45
}
ali@416
    46
ali@416
    47
struct razor_wstr {
ali@416
    48
	wchar_t *str;
ali@416
    49
	int len, allocated;
ali@416
    50
};
ali@416
    51
ali@416
    52
static struct razor_wstr *
ali@416
    53
razor_wstr_create(const char *init, int len)
ali@416
    54
{
ali@416
    55
	int n;
ali@416
    56
	struct razor_wstr *wstr;
ali@416
    57
ali@416
    58
	wstr = malloc(sizeof(struct razor_wstr));
ali@416
    59
ali@416
    60
	n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0);
ali@416
    61
	if (len >= 0 && init[len])
ali@416
    62
		wstr->len = n++;
ali@416
    63
	else
ali@416
    64
		wstr->len = n - 1;
ali@416
    65
ali@416
    66
	wstr->allocated = n * 2;
ali@416
    67
	wstr->str = malloc(wstr->allocated * sizeof(wchar_t));
ali@416
    68
	if (!wstr->str) {
ali@416
    69
		free(wstr);
ali@416
    70
		return NULL;
ali@416
    71
	}
ali@416
    72
ali@416
    73
	(void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n);
ali@416
    74
	if (len >= 0 && init[len])
ali@416
    75
		wstr->str[wstr->len] = 0;
ali@416
    76
ali@416
    77
	return wstr;
ali@416
    78
}
ali@416
    79
ali@416
    80
static int
ali@416
    81
razor_wstr_append(struct razor_wstr *wstr, const char *s, int len)
ali@416
    82
{
ali@416
    83
	int n, allocated;
ali@416
    84
	wchar_t *str;
ali@416
    85
ali@416
    86
	n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0);
ali@416
    87
	if (len < 0 || !s[len])
ali@416
    88
		n--;
ali@416
    89
ali@416
    90
	if (wstr->allocated <= wstr->len + n) {
ali@416
    91
		allocated = (wstr->len + n + 1) * 2;
ali@416
    92
		str = realloc(wstr->str, allocated * sizeof(wchar_t));
ali@416
    93
		if (!str)
ali@416
    94
			return -1;
ali@416
    95
		wstr->allocated = allocated;
ali@416
    96
		wstr->str = str;
ali@416
    97
	}
ali@416
    98
ali@416
    99
	(void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n);
ali@416
   100
	wstr->len += n;
ali@416
   101
	wstr->str[wstr->len] = 0;
ali@416
   102
ali@416
   103
	return 0;
ali@416
   104
}
ali@416
   105
ali@498
   106
#if 0
ali@498
   107
static char *
ali@498
   108
razor_wstr_contents(struct razor_wstr *wstr, int len)
ali@498
   109
{
ali@498
   110
	int n;
ali@498
   111
	char *s;
ali@498
   112
ali@498
   113
	if (len < 0 || len > wstr->len)
ali@498
   114
		len = wstr->len;
ali@498
   115
ali@498
   116
	n = WideCharToMultiByte(CP_UTF8, 0, wstr->str, len, NULL, 0, NULL,
ali@498
   117
				NULL);
ali@498
   118
ali@498
   119
	s = malloc(n + 1);
ali@498
   120
ali@498
   121
	(void)WideCharToMultiByte(CP_UTF8, 0, wstr->str, wstr->len, s, n, NULL,
ali@498
   122
				  NULL);
ali@498
   123
ali@498
   124
	s[n] = '\0';
ali@498
   125
ali@498
   126
	return s;
ali@498
   127
}
ali@498
   128
#endif
ali@498
   129
ali@416
   130
static void
ali@416
   131
razor_wstr_destroy(struct razor_wstr *wstr)
ali@416
   132
{
ali@416
   133
	free(wstr->str);
ali@416
   134
	free(wstr);
ali@416
   135
}
ali@416
   136
ali@416
   137
RAZOR_EXPORT struct razor_atomic *
ali@416
   138
razor_atomic_open(const char *description)
ali@416
   139
{
ali@416
   140
	wchar_t *buf;
ali@416
   141
	struct razor_atomic *atomic;
ali@416
   142
ali@416
   143
	atomic = zalloc(sizeof *atomic);
ali@416
   144
	buf = razor_utf8_to_utf16(description, -1);
ali@416
   145
	atomic->transaction = CreateTransaction(NULL, 0,
ali@416
   146
						TRANSACTION_DO_NOT_PROMOTE,
ali@416
   147
						0, 0, 0, buf);
ali@416
   148
	free(buf);
ali@416
   149
ali@416
   150
	return atomic;
ali@416
   151
}
ali@416
   152
ali@416
   153
RAZOR_EXPORT int
ali@416
   154
razor_atomic_commit(struct razor_atomic *atomic)
ali@416
   155
{
ali@416
   156
	int retval;
ali@416
   157
ali@416
   158
	if (razor_atomic_in_error_state(atomic))
ali@416
   159
		return -1;
ali@416
   160
ali@416
   161
	retval = !CommitTransaction(atomic->transaction);
ali@416
   162
ali@416
   163
	if (retval) {
ali@424
   164
		razor_set_error_mswin(&atomic->error, NULL, GetLastError());
ali@416
   165
		RollbackTransaction(atomic->transaction);
ali@416
   166
	}
ali@416
   167
ali@416
   168
	CloseHandle(atomic->transaction);
ali@416
   169
	atomic->transaction = INVALID_HANDLE_VALUE;
ali@416
   170
ali@416
   171
	return retval;
ali@416
   172
}
ali@416
   173
ali@416
   174
RAZOR_EXPORT void
ali@416
   175
razor_atomic_destroy(struct razor_atomic *atomic)
ali@416
   176
{
ali@416
   177
	int i;
ali@416
   178
ali@416
   179
	for(i = 0; i < atomic->n_files; i++) {
ali@416
   180
		if (atomic->files[i].h != INVALID_HANDLE_VALUE) {
ali@416
   181
			CloseHandle(atomic->files[i].h);
ali@416
   182
			free(atomic->files[i].path);
ali@416
   183
		}
ali@416
   184
	}
ali@416
   185
	free(atomic->files);
ali@416
   186
	if (atomic->transaction != INVALID_HANDLE_VALUE) {
ali@416
   187
		RollbackTransaction(atomic->transaction);
ali@416
   188
		CloseHandle(atomic->transaction);
ali@416
   189
	}
ali@423
   190
	if (atomic->error)
ali@423
   191
		razor_error_free(atomic->error);
ali@416
   192
	free(atomic);
ali@416
   193
}
ali@416
   194
ali@478
   195
/*
ali@478
   196
 * If root_uri is empty, then uri can be a URI (but not a relative-ref; ie.,
ali@478
   197
 * a scheme must be specified). If root_uri is non-empty, then it must be a
ali@478
   198
 * URI (but not a relative-ref) and uri must be a non-empty path.
ali@478
   199
 */
ali@416
   200
RAZOR_EXPORT int
ali@478
   201
razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root_uri,
ali@478
   202
		       const char *uri)
ali@416
   203
{
ali@416
   204
	struct razor_wstr *buffer;
ali@416
   205
	const char *slash, *next;
ali@416
   206
	WIN32_FILE_ATTRIBUTE_DATA fa;
ali@416
   207
	DWORD err;
ali@416
   208
	int r, creating = 0;
ali@478
   209
	struct razor_uri ru;
ali@478
   210
	struct razor_error *tmp_error = NULL;
ali@478
   211
	char *s, *root, *path;
ali@416
   212
ali@416
   213
	if (razor_atomic_in_error_state(atomic))
ali@416
   214
		return -1;
ali@416
   215
ali@478
   216
	if (!*root_uri) {
ali@478
   217
                if (razor_uri_parse(&ru, uri, &tmp_error)) {
ali@478
   218
			razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@478
   219
			return -1;
ali@478
   220
		}
ali@498
   221
		s = ru.path;
ali@478
   222
		if (*s == '/')
ali@498
   223
		{
ali@498
   224
			s = SKIP_DRIVE_LETTER(ru.path + 1);
ali@498
   225
			if (s > ru.path + 1 && *s == '/')
ali@498
   226
				s++;
ali@498
   227
		}
ali@478
   228
		*s = '\0';
ali@478
   229
		s = razor_uri_recompose(&ru);
ali@478
   230
		uri += strlen(s);
ali@478
   231
		free(s);
ali@478
   232
		root = razor_path_from_parsed_uri(&ru, &tmp_error);
ali@478
   233
		razor_uri_destroy(&ru);
ali@478
   234
		if (!root) {
ali@478
   235
			razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@478
   236
			return -1;
ali@478
   237
		}
ali@498
   238
		s = razor_concat("file:", uri, NULL);
ali@498
   239
		path = razor_path_from_uri(s, &tmp_error);
ali@498
   240
		free(s);
ali@478
   241
		if (!path) {
ali@478
   242
			razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@478
   243
			free(root);
ali@478
   244
			return -1;
ali@478
   245
		}
ali@478
   246
	} else {
ali@478
   247
		root = razor_path_from_uri(root_uri, &tmp_error);
ali@478
   248
		if (!root) {
ali@478
   249
			razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@478
   250
			return -1;
ali@478
   251
		}
ali@478
   252
		s = razor_concat("file:", uri, NULL);
ali@478
   253
		path = razor_path_from_uri(s, &tmp_error);
ali@478
   254
		free(s);
ali@478
   255
		if (!path) {
ali@478
   256
			razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@478
   257
			free(root);
ali@478
   258
			return -1;
ali@478
   259
		}
ali@478
   260
		/*
ali@478
   261
		 * Arguably, razor_atomic_make_dirs(a, "file:///", "c:/xxx")
ali@478
   262
		 * is wrong, and we should be called as:
ali@478
   263
		 * razor_atomic_make_dirs(a, "file:///c:/", "xxx")
ali@478
   264
		 * but cope with it anyway.
ali@478
   265
		 */
ali@478
   266
		s = SKIP_DRIVE_LETTER(path);
ali@478
   267
		if (!strcmp(root, "/") && *s == '/') {
ali@478
   268
			free(root);
ali@478
   269
			root = strdup("");
ali@478
   270
		}
ali@478
   271
	}
ali@478
   272
ali@416
   273
	buffer = razor_wstr_create(root, -1);
ali@478
   274
	free(root);
ali@441
   275
	slash = buffer->len ? SKIP_DRIVE_LETTER(path) : path;
ali@416
   276
ali@416
   277
	for (; *slash != '\0'; slash = next) {
ali@478
   278
		next = strchr(slash + 1, '/');
ali@416
   279
		if (next == NULL)
ali@416
   280
			break;
ali@416
   281
ali@416
   282
		razor_wstr_append(buffer, slash, next - slash);
ali@416
   283
ali@416
   284
		if (!creating) {
ali@416
   285
			if (razor_valid_root_name2(buffer->str))
ali@416
   286
				continue;
ali@416
   287
ali@416
   288
			r = GetFileAttributesTransactedW(buffer->str,
ali@416
   289
							 GetFileExInfoStandard,
ali@416
   290
							 &fa,
ali@416
   291
							 atomic->transaction);
ali@416
   292
ali@416
   293
			if (!r) {
ali@416
   294
				err = GetLastError();
ali@416
   295
				if (err == ERROR_FILE_NOT_FOUND) {
ali@416
   296
					creating = 1;
ali@416
   297
				} else {
ali@424
   298
					razor_set_error_mswin(&atomic->error,
ali@424
   299
							      buffer->str, err);
ali@416
   300
					razor_wstr_destroy(buffer);
ali@478
   301
					free(path);
ali@416
   302
					return -1;
ali@416
   303
				}
ali@416
   304
			} else if (!(fa.dwFileAttributes&
ali@416
   305
				     FILE_ATTRIBUTE_DIRECTORY)) {
ali@447
   306
				razor_set_error2(&atomic->error,
ali@447
   307
						 RAZOR_MSWIN_ERROR,
ali@447
   308
						 ERROR_DIRECTORY, buffer->str,
ali@424
   309
						 "Not a directory");
ali@416
   310
				razor_wstr_destroy(buffer);
ali@478
   311
				free(path);
ali@416
   312
				return -1;
ali@416
   313
			}
ali@416
   314
		}
ali@416
   315
		if (creating) {
ali@416
   316
			if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL,
ali@416
   317
							atomic->transaction)) {
ali@424
   318
				razor_set_error_mswin(&atomic->error,
ali@424
   319
						      buffer->str,
ali@424
   320
						      GetLastError());
ali@416
   321
				razor_wstr_destroy(buffer);
ali@478
   322
				free(path);
ali@416
   323
				return -1;
ali@416
   324
			}
ali@416
   325
ali@416
   326
			/* FIXME: What to do about permissions for dirs we
ali@416
   327
			 * have to create but are not in the cpio archive? */
ali@416
   328
		}
ali@416
   329
	}
ali@416
   330
ali@416
   331
	razor_wstr_destroy(buffer);
ali@478
   332
	free(path);
ali@416
   333
ali@416
   334
	return 0;
ali@416
   335
}
ali@416
   336
ali@416
   337
RAZOR_EXPORT int
ali@478
   338
razor_atomic_remove(struct razor_atomic *atomic, const char *uri)
ali@416
   339
{
ali@478
   340
	struct razor_error *tmp_error = NULL;
ali@478
   341
	char *path;
ali@416
   342
	wchar_t *buf;
ali@416
   343
	DWORD err;
ali@416
   344
ali@416
   345
	if (razor_atomic_in_error_state(atomic))
ali@416
   346
		return -1;
ali@416
   347
ali@478
   348
	path = razor_path_from_uri(uri, &tmp_error);
ali@478
   349
	if (!path) {
ali@478
   350
		razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@478
   351
		return -1;
ali@478
   352
	}
ali@478
   353
ali@416
   354
	buf = razor_utf8_to_utf16(path, -1);
ali@416
   355
ali@478
   356
	free(path);
ali@478
   357
ali@416
   358
	if (DeleteFileTransactedW(buf, atomic->transaction)) {
ali@416
   359
		free(buf);
ali@416
   360
		return 0;
ali@416
   361
	}
ali@416
   362
ali@416
   363
	err = GetLastError();
ali@416
   364
	if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
ali@416
   365
		free(buf);
ali@416
   366
		return 0;
ali@416
   367
	}
ali@416
   368
ali@416
   369
	if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL,
ali@416
   370
					 atomic->transaction)) {
ali@416
   371
		if (DeleteFileTransactedW(buf, atomic->transaction)) {
ali@416
   372
			free(buf);
ali@416
   373
			return 0;
ali@416
   374
		}
ali@416
   375
		err = GetLastError();
ali@416
   376
	}
ali@416
   377
ali@416
   378
	if (RemoveDirectoryTransactedW(buf, atomic->transaction) ||
ali@416
   379
	    GetLastError() == ERROR_DIR_NOT_EMPTY) {
ali@416
   380
		free(buf);
ali@416
   381
		return 0;
ali@416
   382
	}
ali@416
   383
ali@416
   384
	/*
ali@416
   385
	 * It would be tempting to use:
ali@416
   386
	 * 	MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)
ali@416
   387
	 * but unless we can guarantee that the system will be rebooted
ali@416
   388
	 * before we (or some other application) write another file with the
ali@416
   389
	 * same path, this is likely to cause more problems than it solves.
ali@416
   390
	 */
ali@416
   391
ali@424
   392
	razor_set_error_mswin(&atomic->error, buf, err);
ali@416
   393
	free(buf);
ali@416
   394
	return -1;
ali@416
   395
}
ali@416
   396
ali@416
   397
RAZOR_EXPORT int
ali@478
   398
razor_atomic_rename_file(struct razor_atomic *atomic, const char *old_uri,
ali@478
   399
			 const char *new_uri)
ali@416
   400
{
ali@478
   401
	struct razor_error *tmp_error = NULL;
ali@478
   402
	char *oldpath, *newpath;
ali@416
   403
	wchar_t *oldbuf, *newbuf;
ali@416
   404
	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
ali@416
   405
ali@416
   406
	if (razor_atomic_in_error_state(atomic))
ali@416
   407
		return -1;
ali@416
   408
ali@478
   409
	newpath = razor_path_from_uri(new_uri, &tmp_error);
ali@478
   410
	if (!newpath) {
ali@478
   411
		razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@478
   412
		return -1;
ali@478
   413
	}
ali@478
   414
ali@478
   415
	oldpath = razor_path_from_uri(old_uri, &tmp_error);
ali@478
   416
	if (!oldpath) {
ali@478
   417
		razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@478
   418
		free(newpath);
ali@478
   419
		return -1;
ali@478
   420
	}
ali@478
   421
ali@416
   422
	newbuf = razor_utf8_to_utf16(newpath, -1);
ali@416
   423
	oldbuf = razor_utf8_to_utf16(oldpath, -1);
ali@416
   424
ali@478
   425
	free(newpath);
ali@478
   426
	free(oldpath);
ali@478
   427
ali@416
   428
	/*
ali@416
   429
	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will
ali@416
   430
	 * cover every case we care about _except_ replacing an empty
ali@416
   431
	 * directory with a file. Calling RemoveDirectoryTransacted() will deal
ali@416
   432
	 * with this case while having no effect in all other cases.
ali@416
   433
	 */
ali@416
   434
	(void)RemoveDirectoryTransactedW(newbuf, atomic->transaction);
ali@416
   435
ali@416
   436
	if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags,
ali@416
   437
			         atomic->transaction))
ali@424
   438
		razor_set_error_mswin(&atomic->error, newbuf, GetLastError());
ali@416
   439
ali@416
   440
	free(newbuf);
ali@416
   441
	free(oldbuf);
ali@416
   442
ali@423
   443
	return razor_atomic_in_error_state(atomic);
ali@416
   444
}
ali@416
   445
ali@416
   446
RAZOR_EXPORT int
ali@478
   447
razor_atomic_create_dir(struct razor_atomic *atomic, const char *uri,
ali@416
   448
			mode_t mode)
ali@416
   449
{
ali@478
   450
	struct razor_error *tmp_error = NULL;
ali@478
   451
	char *dirname;
ali@416
   452
	wchar_t *buf;
ali@416
   453
	DWORD err;
ali@416
   454
	WIN32_FILE_ATTRIBUTE_DATA fa;
ali@416
   455
ali@416
   456
	if (razor_atomic_in_error_state(atomic))
ali@416
   457
		return -1;
ali@416
   458
ali@478
   459
	dirname = razor_path_from_uri(uri, &tmp_error);
ali@478
   460
	if (!dirname) {
ali@478
   461
		razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@478
   462
		return -1;
ali@478
   463
	}
ali@478
   464
ali@416
   465
	buf = razor_utf8_to_utf16(dirname, -1);
ali@416
   466
ali@478
   467
	free(dirname);
ali@478
   468
ali@416
   469
	if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) {
ali@416
   470
		err = GetLastError();
ali@416
   471
		if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) {
ali@416
   472
abort:
ali@424
   473
			razor_set_error_mswin(&atomic->error, buf, err);
ali@416
   474
			free(buf);
ali@416
   475
			return -1;
ali@416
   476
		}
ali@416
   477
ali@416
   478
		if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard,
ali@416
   479
						  &fa, atomic->transaction))
ali@416
   480
			goto abort;
ali@416
   481
ali@416
   482
		if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) {
ali@494
   483
			if (razor_atomic_remove(atomic, uri)) {
ali@416
   484
				free(buf);
ali@416
   485
				return -1;
ali@416
   486
			}
ali@416
   487
			if (!CreateDirectoryTransactedW(NULL, buf, NULL,
ali@416
   488
							atomic->transaction)) {
ali@416
   489
				err = GetLastError();
ali@416
   490
				goto abort;
ali@416
   491
			}
ali@416
   492
		}
ali@416
   493
	}
ali@416
   494
ali@416
   495
	free(buf);
ali@416
   496
ali@416
   497
	return 0;
ali@416
   498
}
ali@416
   499
ali@416
   500
RAZOR_EXPORT int
ali@416
   501
razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
ali@478
   502
			    const char *uri)
ali@416
   503
{
ali@416
   504
	if (razor_atomic_in_error_state(atomic))
ali@416
   505
		return -1;
ali@416
   506
ali@416
   507
	/*
ali@416
   508
	 * This isn't true, but symbolic links under Windows 7
ali@416
   509
	 * need to know whether the target is a directory or not
ali@416
   510
	 * and we don't always know that at the time when the
ali@416
   511
	 * link is created, so it's a convienent lie for now.
ali@416
   512
	 */
ali@447
   513
	razor_set_error(&atomic->error, RAZOR_MSWIN_ERROR, ERROR_NOT_SUPPORTED,
ali@447
   514
			NULL, "Symbolic links not supported on this platform");
ali@416
   515
ali@416
   516
	return -1;
ali@416
   517
}
ali@416
   518
ali@416
   519
RAZOR_EXPORT int
ali@478
   520
razor_atomic_create_file(struct razor_atomic *atomic, const char *uri,
ali@416
   521
			 mode_t mode)
ali@416
   522
{
ali@478
   523
	struct razor_error *tmp_error = NULL;
ali@478
   524
	char *filename;
ali@416
   525
	DWORD attribs;
ali@416
   526
	struct razor_atomic_file *files;
ali@416
   527
	int i = atomic->n_files;
ali@416
   528
ali@416
   529
	if (razor_atomic_in_error_state(atomic))
ali@416
   530
		return -1;
ali@416
   531
ali@478
   532
	filename = razor_path_from_uri(uri, &tmp_error);
ali@478
   533
	if (!filename) {
ali@478
   534
		razor_atomic_propagate_error(atomic, tmp_error, NULL);
ali@478
   535
		return -1;
ali@478
   536
	}
ali@478
   537
ali@416
   538
	files = realloc(atomic->files,
ali@416
   539
			(atomic->n_files+1) * sizeof(struct razor_atomic_file));
ali@416
   540
	if (!files) {
ali@447
   541
		razor_set_error(&atomic->error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
ali@447
   542
				"Not enough memory");
ali@478
   543
		free(filename);
ali@416
   544
		return -1;
ali@416
   545
	}
ali@416
   546
	atomic->n_files++;
ali@416
   547
	atomic->files = files;
ali@416
   548
ali@416
   549
	files[i].path = razor_utf8_to_utf16(filename, -1);
ali@416
   550
ali@478
   551
	free(filename);
ali@478
   552
ali@416
   553
	/*
ali@416
   554
	 * Passing CREATE_ALWAYS to CreateFileTransacted() will cover
ali@416
   555
	 * every case we care about _except_ replacing an empty directory
ali@416
   556
	 * with a file. Calling RemoveDirectoryTransacted() will deal
ali@416
   557
	 * with this case while having no effect in all other cases.
ali@416
   558
	 */
ali@416
   559
	(void)RemoveDirectoryTransactedW(files[i].path, atomic->transaction);
ali@416
   560
ali@416
   561
	if (mode & S_IWUSR)
ali@416
   562
		attribs = FILE_ATTRIBUTE_NORMAL;
ali@416
   563
	else
ali@416
   564
		attribs = FILE_ATTRIBUTE_READONLY;
ali@416
   565
ali@416
   566
	files[i].h = CreateFileTransactedW(files[i].path, GENERIC_WRITE,
ali@416
   567
					   0, NULL, CREATE_ALWAYS, attribs,
ali@416
   568
					   NULL, atomic->transaction, NULL,
ali@416
   569
					   NULL);
ali@416
   570
ali@416
   571
	if (files[i].h == INVALID_HANDLE_VALUE) {
ali@424
   572
		razor_set_error_mswin(&atomic->error, files[i].path,
ali@424
   573
				      GetLastError());
ali@416
   574
		free(files[i].path);
ali@416
   575
		atomic->n_files--;
ali@416
   576
		return -1;
ali@416
   577
	}
ali@416
   578
ali@416
   579
	return i;
ali@416
   580
}
ali@416
   581
ali@416
   582
RAZOR_EXPORT int
ali@416
   583
razor_atomic_write(struct razor_atomic *atomic, int handle, const void *data,
ali@416
   584
		   size_t size)
ali@416
   585
{
ali@416
   586
	DWORD written;
ali@416
   587
ali@416
   588
	if (razor_atomic_in_error_state(atomic))
ali@416
   589
		return -1;
ali@416
   590
ali@416
   591
	assert(handle < atomic->n_files);
ali@416
   592
	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
ali@416
   593
ali@416
   594
	while(size) {
ali@416
   595
		if (!WriteFile(atomic->files[handle].h, data, size, &written,
ali@416
   596
			       NULL)) {
ali@424
   597
			razor_set_error_mswin(&atomic->error,
ali@424
   598
					      atomic->files[handle].path,
ali@424
   599
					      GetLastError());
ali@416
   600
ali@416
   601
			(void)CloseHandle(atomic->files[handle].h);
ali@416
   602
			free(atomic->files[handle].path);
ali@416
   603
			atomic->files[handle].path = NULL;
ali@416
   604
			atomic->files[handle].h = INVALID_HANDLE_VALUE;
ali@416
   605
ali@416
   606
			return -1;
ali@416
   607
		}
ali@416
   608
ali@416
   609
		data += written;
ali@416
   610
		size -= written;
ali@416
   611
	}
ali@416
   612
ali@416
   613
	return 0;
ali@416
   614
}
ali@416
   615
ali@416
   616
RAZOR_EXPORT int
ali@416
   617
razor_atomic_sync(struct razor_atomic *atomic, int handle)
ali@416
   618
{
ali@416
   619
	HANDLE h;
ali@416
   620
ali@416
   621
	if (razor_atomic_in_error_state(atomic))
ali@416
   622
		return -1;
ali@416
   623
ali@416
   624
	assert(handle < atomic->n_files);
ali@416
   625
	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
ali@416
   626
ali@416
   627
	if (!CloseHandle(atomic->files[handle].h)) {
ali@424
   628
		razor_set_error_mswin(&atomic->error,
ali@424
   629
				      atomic->files[handle].path,
ali@424
   630
				      GetLastError());
ali@416
   631
		free(atomic->files[handle].path);
ali@416
   632
		atomic->files[handle].path = NULL;
ali@416
   633
		atomic->files[handle].h = INVALID_HANDLE_VALUE;
ali@416
   634
		return -1;
ali@416
   635
	}
ali@416
   636
ali@416
   637
	h = CreateFileTransactedW(atomic->files[handle].path, GENERIC_WRITE, 0,
ali@416
   638
				  NULL, OPEN_EXISTING, 0, NULL,
ali@416
   639
				  atomic->transaction, NULL, NULL);
ali@416
   640
	atomic->files[handle].h = h;
ali@416
   641
ali@416
   642
	if (atomic->files[handle].h == INVALID_HANDLE_VALUE) {
ali@424
   643
		razor_set_error_mswin(&atomic->error,
ali@424
   644
				      atomic->files[handle].path,
ali@424
   645
				      GetLastError());
ali@416
   646
		free(atomic->files[handle].path);
ali@416
   647
		atomic->files[handle].path = NULL;
ali@416
   648
		return -1;
ali@416
   649
	}
ali@416
   650
ali@423
   651
	return razor_atomic_in_error_state(atomic);
ali@416
   652
}
ali@416
   653
ali@416
   654
RAZOR_EXPORT int
ali@416
   655
razor_atomic_close(struct razor_atomic *atomic, int handle)
ali@416
   656
{
ali@416
   657
	if (razor_atomic_in_error_state(atomic))
ali@416
   658
		return -1;
ali@416
   659
ali@416
   660
	assert(handle < atomic->n_files);
ali@416
   661
	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
ali@416
   662
ali@416
   663
	if (!CloseHandle(atomic->files[handle].h))
ali@424
   664
		razor_set_error_mswin(&atomic->error,
ali@424
   665
				      atomic->files[handle].path,
ali@424
   666
				      GetLastError());
ali@416
   667
ali@416
   668
	free(atomic->files[handle].path);
ali@416
   669
	atomic->files[handle].path = NULL;
ali@416
   670
	atomic->files[handle].h = INVALID_HANDLE_VALUE;
ali@416
   671
ali@416
   672
	while(atomic->n_files > 0 &&
ali@416
   673
	      atomic->files[atomic->n_files-1].h == INVALID_HANDLE_VALUE)
ali@416
   674
		atomic->n_files--;
ali@416
   675
ali@423
   676
	return razor_atomic_in_error_state(atomic);
ali@416
   677
}
ali@416
   678
ali@416
   679
#endif		/* HAVE_WINDOWS_KTM */