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