librazor/atomic-ktm.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Aug 23 11:13:48 2014 +0100 (2014-08-23)
changeset 440 48204dea0b9f
parent 423 6112bcc5d1cf
child 441 cf499fd51df7
permissions -rw-r--r--
Remove INTLLIBS from librazor_la_LIBADD.

This partially reverts 611c84a3f4b4538a65d186050608c17adbf17770.
It's not clear what motivated the initial inclusion of INTLLIBS
here since the net effect is only seen in librazor.la and not
in razor.pc and librazor.la is not normally packaged. Certainly
neither the static nor the dynamic versions of librazor currently
use libintl. At best this would cause the linker to search a
static libintl for undefined symbols without finding any; at worse
it causes a static build of plover using librazor.la to fail if
no static version of libintl is installed.
     1 /*
     2  * Copyright (C) 2011-2012  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 <errno.h>
    28 #include <unistd.h>
    29 #include <fcntl.h>
    30 #include <sys/stat.h>
    31 #include <string.h>
    32 #include <assert.h>
    33 #include <wchar.h>
    34 #include <ktmw32.h>
    35 
    36 #include "razor.h"
    37 #include "razor-internal.h"
    38 
    39 #define RAZOR_ASCII_ISALPHA(c)	\
    40 			((c) >= 'A' && (c) <= 'Z' || (c) >= 'a' && (c) <= 'z')
    41 
    42 static int
    43 razor_valid_root_name2(const wchar_t *name)
    44 {
    45 	if (razor_allow_all_root_names())
    46 		return !wcschr(name, '/');
    47 
    48 	return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
    49 	       name[2] == '\0';
    50 }
    51 
    52 struct razor_wstr {
    53 	wchar_t *str;
    54 	int len, allocated;
    55 };
    56 
    57 static struct razor_wstr *
    58 razor_wstr_create(const char *init, int len)
    59 {
    60 	int n;
    61 	struct razor_wstr *wstr;
    62 
    63 	wstr = malloc(sizeof(struct razor_wstr));
    64 
    65 	n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0);
    66 	if (len >= 0 && init[len])
    67 		wstr->len = n++;
    68 	else
    69 		wstr->len = n - 1;
    70 
    71 	wstr->allocated = n * 2;
    72 	wstr->str = malloc(wstr->allocated * sizeof(wchar_t));
    73 	if (!wstr->str) {
    74 		free(wstr);
    75 		return NULL;
    76 	}
    77 
    78 	(void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n);
    79 	if (len >= 0 && init[len])
    80 		wstr->str[wstr->len] = 0;
    81 
    82 	return wstr;
    83 }
    84 
    85 static int
    86 razor_wstr_append(struct razor_wstr *wstr, const char *s, int len)
    87 {
    88 	int n, allocated;
    89 	wchar_t *str;
    90 
    91 	n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0);
    92 	if (len < 0 || !s[len])
    93 		n--;
    94 
    95 	if (wstr->allocated <= wstr->len + n) {
    96 		allocated = (wstr->len + n + 1) * 2;
    97 		str = realloc(wstr->str, allocated * sizeof(wchar_t));
    98 		if (!str)
    99 			return -1;
   100 		wstr->allocated = allocated;
   101 		wstr->str = str;
   102 	}
   103 
   104 	(void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n);
   105 	wstr->len += n;
   106 	wstr->str[wstr->len] = 0;
   107 
   108 	return 0;
   109 }
   110 
   111 static void
   112 razor_wstr_destroy(struct razor_wstr *wstr)
   113 {
   114 	free(wstr->str);
   115 	free(wstr);
   116 }
   117 
   118 RAZOR_EXPORT struct razor_atomic *
   119 razor_atomic_open(const char *description)
   120 {
   121 	wchar_t *buf;
   122 	struct razor_atomic *atomic;
   123 
   124 	atomic = zalloc(sizeof *atomic);
   125 	buf = razor_utf8_to_utf16(description, -1);
   126 	atomic->transaction = CreateTransaction(NULL, 0,
   127 						TRANSACTION_DO_NOT_PROMOTE,
   128 						0, 0, 0, buf);
   129 	free(buf);
   130 
   131 	return atomic;
   132 }
   133 
   134 RAZOR_EXPORT int
   135 razor_atomic_commit(struct razor_atomic *atomic)
   136 {
   137 	int retval;
   138 
   139 	if (razor_atomic_in_error_state(atomic))
   140 		return -1;
   141 
   142 	retval = !CommitTransaction(atomic->transaction);
   143 
   144 	if (retval) {
   145 		razor_set_error_mswin(&atomic->error, NULL, GetLastError());
   146 		RollbackTransaction(atomic->transaction);
   147 	}
   148 
   149 	CloseHandle(atomic->transaction);
   150 	atomic->transaction = INVALID_HANDLE_VALUE;
   151 
   152 	return retval;
   153 }
   154 
   155 RAZOR_EXPORT void
   156 razor_atomic_destroy(struct razor_atomic *atomic)
   157 {
   158 	int i;
   159 
   160 	for(i = 0; i < atomic->n_files; i++) {
   161 		if (atomic->files[i].h != INVALID_HANDLE_VALUE) {
   162 			CloseHandle(atomic->files[i].h);
   163 			free(atomic->files[i].path);
   164 		}
   165 	}
   166 	free(atomic->files);
   167 	if (atomic->transaction != INVALID_HANDLE_VALUE) {
   168 		RollbackTransaction(atomic->transaction);
   169 		CloseHandle(atomic->transaction);
   170 	}
   171 	if (atomic->error)
   172 		razor_error_free(atomic->error);
   173 	free(atomic);
   174 }
   175 
   176 RAZOR_EXPORT int
   177 razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
   178 		       const char *path)
   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 
   186 	if (razor_atomic_in_error_state(atomic))
   187 		return -1;
   188 
   189 	buffer = razor_wstr_create(root, -1);
   190 	slash = path;
   191 
   192 	for (; *slash != '\0'; slash = next) {
   193 		next = strpbrk(slash + 1, "/\\");
   194 		if (next == NULL)
   195 			break;
   196 
   197 		razor_wstr_append(buffer, slash, next - slash);
   198 
   199 		if (!creating) {
   200 			if (razor_valid_root_name2(buffer->str))
   201 				continue;
   202 
   203 			r = GetFileAttributesTransactedW(buffer->str,
   204 							 GetFileExInfoStandard,
   205 							 &fa,
   206 							 atomic->transaction);
   207 
   208 			if (!r) {
   209 				err = GetLastError();
   210 				if (err == ERROR_FILE_NOT_FOUND) {
   211 					creating = 1;
   212 				} else {
   213 					razor_set_error_mswin(&atomic->error,
   214 							      buffer->str, err);
   215 					razor_wstr_destroy(buffer);
   216 					return -1;
   217 				}
   218 			} else if (!(fa.dwFileAttributes&
   219 				     FILE_ATTRIBUTE_DIRECTORY)) {
   220 				razor_set_error2(&atomic->error, buffer->str,
   221 						 "Not a directory");
   222 				razor_wstr_destroy(buffer);
   223 				return -1;
   224 			}
   225 		}
   226 		if (creating) {
   227 			if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL,
   228 							atomic->transaction)) {
   229 				razor_set_error_mswin(&atomic->error,
   230 						      buffer->str,
   231 						      GetLastError());
   232 				razor_wstr_destroy(buffer);
   233 				return -1;
   234 			}
   235 
   236 			/* FIXME: What to do about permissions for dirs we
   237 			 * have to create but are not in the cpio archive? */
   238 		}
   239 	}
   240 
   241 	razor_wstr_destroy(buffer);
   242 
   243 	return 0;
   244 }
   245 
   246 RAZOR_EXPORT int
   247 razor_atomic_remove(struct razor_atomic *atomic, const char *path)
   248 {
   249 	wchar_t *buf;
   250 	DWORD err;
   251 
   252 	if (razor_atomic_in_error_state(atomic))
   253 		return -1;
   254 
   255 	buf = razor_utf8_to_utf16(path, -1);
   256 
   257 	if (DeleteFileTransactedW(buf, atomic->transaction)) {
   258 		free(buf);
   259 		return 0;
   260 	}
   261 
   262 	err = GetLastError();
   263 	if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
   264 		free(buf);
   265 		return 0;
   266 	}
   267 
   268 	if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL,
   269 					 atomic->transaction)) {
   270 		if (DeleteFileTransactedW(buf, atomic->transaction)) {
   271 			free(buf);
   272 			return 0;
   273 		}
   274 		err = GetLastError();
   275 	}
   276 
   277 	if (RemoveDirectoryTransactedW(buf, atomic->transaction) ||
   278 	    GetLastError() == ERROR_DIR_NOT_EMPTY) {
   279 		free(buf);
   280 		return 0;
   281 	}
   282 
   283 	/*
   284 	 * It would be tempting to use:
   285 	 * 	MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)
   286 	 * but unless we can guarantee that the system will be rebooted
   287 	 * before we (or some other application) write another file with the
   288 	 * same path, this is likely to cause more problems than it solves.
   289 	 */
   290 
   291 	razor_set_error_mswin(&atomic->error, buf, err);
   292 	free(buf);
   293 	return -1;
   294 }
   295 
   296 RAZOR_EXPORT int
   297 razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
   298 			 const char *newpath)
   299 {
   300 	wchar_t *oldbuf, *newbuf;
   301 	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
   302 
   303 	if (razor_atomic_in_error_state(atomic))
   304 		return -1;
   305 
   306 	newbuf = razor_utf8_to_utf16(newpath, -1);
   307 	oldbuf = razor_utf8_to_utf16(oldpath, -1);
   308 
   309 	/*
   310 	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will
   311 	 * cover every case we care about _except_ replacing an empty
   312 	 * directory with a file. Calling RemoveDirectoryTransacted() will deal
   313 	 * with this case while having no effect in all other cases.
   314 	 */
   315 	(void)RemoveDirectoryTransactedW(newbuf, atomic->transaction);
   316 
   317 	if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags,
   318 			         atomic->transaction))
   319 		razor_set_error_mswin(&atomic->error, newbuf, GetLastError());
   320 
   321 	free(newbuf);
   322 	free(oldbuf);
   323 
   324 	return razor_atomic_in_error_state(atomic);
   325 }
   326 
   327 RAZOR_EXPORT int
   328 razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
   329 			mode_t mode)
   330 {
   331 	wchar_t *buf;
   332 	DWORD err;
   333 	WIN32_FILE_ATTRIBUTE_DATA fa;
   334 
   335 	if (razor_atomic_in_error_state(atomic))
   336 		return -1;
   337 
   338 	buf = razor_utf8_to_utf16(dirname, -1);
   339 
   340 	if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) {
   341 		err = GetLastError();
   342 		if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) {
   343 abort:
   344 			razor_set_error_mswin(&atomic->error, buf, err);
   345 			free(buf);
   346 			return -1;
   347 		}
   348 
   349 		if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard,
   350 						  &fa, atomic->transaction))
   351 			goto abort;
   352 
   353 		if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) {
   354 			if (razor_atomic_remove(atomic, dirname)) {
   355 				free(buf);
   356 				return -1;
   357 			}
   358 			if (!CreateDirectoryTransactedW(NULL, buf, NULL,
   359 							atomic->transaction)) {
   360 				err = GetLastError();
   361 				goto abort;
   362 			}
   363 		}
   364 	}
   365 
   366 	free(buf);
   367 
   368 	return 0;
   369 }
   370 
   371 RAZOR_EXPORT int
   372 razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
   373 			    const char *path)
   374 {
   375 	if (razor_atomic_in_error_state(atomic))
   376 		return -1;
   377 
   378 	/*
   379 	 * This isn't true, but symbolic links under Windows 7
   380 	 * need to know whether the target is a directory or not
   381 	 * and we don't always know that at the time when the
   382 	 * link is created, so it's a convienent lie for now.
   383 	 */
   384 	razor_set_error(&atomic->error, NULL,
   385 			"Symbolic links not supported on this platform");
   386 
   387 	return -1;
   388 }
   389 
   390 RAZOR_EXPORT int
   391 razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
   392 			 mode_t mode)
   393 {
   394 	DWORD attribs;
   395 	struct razor_atomic_file *files;
   396 	int i = atomic->n_files;
   397 
   398 	if (razor_atomic_in_error_state(atomic))
   399 		return -1;
   400 
   401 	files = realloc(atomic->files,
   402 			(atomic->n_files+1) * sizeof(struct razor_atomic_file));
   403 	if (!files) {
   404 		razor_set_error(&atomic->error, NULL, "Not enough memory");
   405 		return -1;
   406 	}
   407 	atomic->n_files++;
   408 	atomic->files = files;
   409 
   410 	files[i].path = razor_utf8_to_utf16(filename, -1);
   411 
   412 	/*
   413 	 * Passing CREATE_ALWAYS to CreateFileTransacted() will cover
   414 	 * every case we care about _except_ replacing an empty directory
   415 	 * with a file. Calling RemoveDirectoryTransacted() will deal
   416 	 * with this case while having no effect in all other cases.
   417 	 */
   418 	(void)RemoveDirectoryTransactedW(files[i].path, atomic->transaction);
   419 
   420 	if (mode & S_IWUSR)
   421 		attribs = FILE_ATTRIBUTE_NORMAL;
   422 	else
   423 		attribs = FILE_ATTRIBUTE_READONLY;
   424 
   425 	files[i].h = CreateFileTransactedW(files[i].path, GENERIC_WRITE,
   426 					   0, NULL, CREATE_ALWAYS, attribs,
   427 					   NULL, atomic->transaction, NULL,
   428 					   NULL);
   429 
   430 	if (files[i].h == INVALID_HANDLE_VALUE) {
   431 		razor_set_error_mswin(&atomic->error, files[i].path,
   432 				      GetLastError());
   433 		free(files[i].path);
   434 		atomic->n_files--;
   435 		return -1;
   436 	}
   437 
   438 	return i;
   439 }
   440 
   441 RAZOR_EXPORT int
   442 razor_atomic_write(struct razor_atomic *atomic, int handle, const void *data,
   443 		   size_t size)
   444 {
   445 	DWORD written;
   446 
   447 	if (razor_atomic_in_error_state(atomic))
   448 		return -1;
   449 
   450 	assert(handle < atomic->n_files);
   451 	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
   452 
   453 	while(size) {
   454 		if (!WriteFile(atomic->files[handle].h, data, size, &written,
   455 			       NULL)) {
   456 			razor_set_error_mswin(&atomic->error,
   457 					      atomic->files[handle].path,
   458 					      GetLastError());
   459 
   460 			(void)CloseHandle(atomic->files[handle].h);
   461 			free(atomic->files[handle].path);
   462 			atomic->files[handle].path = NULL;
   463 			atomic->files[handle].h = INVALID_HANDLE_VALUE;
   464 
   465 			return -1;
   466 		}
   467 
   468 		data += written;
   469 		size -= written;
   470 	}
   471 
   472 	return 0;
   473 }
   474 
   475 RAZOR_EXPORT int
   476 razor_atomic_sync(struct razor_atomic *atomic, int handle)
   477 {
   478 	HANDLE h;
   479 
   480 	if (razor_atomic_in_error_state(atomic))
   481 		return -1;
   482 
   483 	assert(handle < atomic->n_files);
   484 	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
   485 
   486 	if (!CloseHandle(atomic->files[handle].h)) {
   487 		razor_set_error_mswin(&atomic->error,
   488 				      atomic->files[handle].path,
   489 				      GetLastError());
   490 		free(atomic->files[handle].path);
   491 		atomic->files[handle].path = NULL;
   492 		atomic->files[handle].h = INVALID_HANDLE_VALUE;
   493 		return -1;
   494 	}
   495 
   496 	h = CreateFileTransactedW(atomic->files[handle].path, GENERIC_WRITE, 0,
   497 				  NULL, OPEN_EXISTING, 0, NULL,
   498 				  atomic->transaction, NULL, NULL);
   499 	atomic->files[handle].h = h;
   500 
   501 	if (atomic->files[handle].h == INVALID_HANDLE_VALUE) {
   502 		razor_set_error_mswin(&atomic->error,
   503 				      atomic->files[handle].path,
   504 				      GetLastError());
   505 		free(atomic->files[handle].path);
   506 		atomic->files[handle].path = NULL;
   507 		return -1;
   508 	}
   509 
   510 	return razor_atomic_in_error_state(atomic);
   511 }
   512 
   513 RAZOR_EXPORT int
   514 razor_atomic_close(struct razor_atomic *atomic, int handle)
   515 {
   516 	if (razor_atomic_in_error_state(atomic))
   517 		return -1;
   518 
   519 	assert(handle < atomic->n_files);
   520 	assert(atomic->files[handle].h != INVALID_HANDLE_VALUE);
   521 
   522 	if (!CloseHandle(atomic->files[handle].h))
   523 		razor_set_error_mswin(&atomic->error,
   524 				      atomic->files[handle].path,
   525 				      GetLastError());
   526 
   527 	free(atomic->files[handle].path);
   528 	atomic->files[handle].path = NULL;
   529 	atomic->files[handle].h = INVALID_HANDLE_VALUE;
   530 
   531 	while(atomic->n_files > 0 &&
   532 	      atomic->files[atomic->n_files-1].h == INVALID_HANDLE_VALUE)
   533 		atomic->n_files--;
   534 
   535 	return razor_atomic_in_error_state(atomic);
   536 }
   537 
   538 #endif		/* HAVE_WINDOWS_KTM */