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