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