librazor/atomic-ktm.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Oct 04 18:12:58 2014 +0100 (2014-10-04)
changeset 454 56ff755c268c
parent 441 cf499fd51df7
child 475 008c75a5e08d
permissions -rw-r--r--
Only export symbols starting with razor_ in dynamic library.

Apart from being good practice to avoid clashes with higher-level
libraries and the application, this also fixes an obscure bug: The
gnulib library is used both by librazor (the dynamic library) and
by razor (the executable). In doing so, we want to have two separate
copies of the library despite the code duplication this involves.
Without the explicit limit to export only razor_ symbols, the razor
executable under mingw64 was picking up the getopt_long function
from librazor and the optind variable from libgnu which meant that
it did not see optind changing. Hiding librazor's copy of getopt
causes the linker to find libgnu's copy and everything works.

Note that under mingw librazor-#.dll still contains undocumented
(private) razor_ symbols but these will do no harm as long as nobody
tries to use them.
     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 <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 static int
    40 razor_valid_root_name2(const wchar_t *name)
    41 {
    42 	if (razor_allow_all_root_names())
    43 		return !wcschr(name, '/');
    44 
    45 	return RAZOR_ASCII_ISALPHA(name[0]) && name[1] == ':' &&
    46 	       name[2] == '\0';
    47 }
    48 
    49 struct razor_wstr {
    50 	wchar_t *str;
    51 	int len, allocated;
    52 };
    53 
    54 static struct razor_wstr *
    55 razor_wstr_create(const char *init, int len)
    56 {
    57 	int n;
    58 	struct razor_wstr *wstr;
    59 
    60 	wstr = malloc(sizeof(struct razor_wstr));
    61 
    62 	n = MultiByteToWideChar(CP_UTF8, 0, init, len, NULL, 0);
    63 	if (len >= 0 && init[len])
    64 		wstr->len = n++;
    65 	else
    66 		wstr->len = n - 1;
    67 
    68 	wstr->allocated = n * 2;
    69 	wstr->str = malloc(wstr->allocated * sizeof(wchar_t));
    70 	if (!wstr->str) {
    71 		free(wstr);
    72 		return NULL;
    73 	}
    74 
    75 	(void)MultiByteToWideChar(CP_UTF8, 0, init, len, wstr->str, n);
    76 	if (len >= 0 && init[len])
    77 		wstr->str[wstr->len] = 0;
    78 
    79 	return wstr;
    80 }
    81 
    82 static int
    83 razor_wstr_append(struct razor_wstr *wstr, const char *s, int len)
    84 {
    85 	int n, allocated;
    86 	wchar_t *str;
    87 
    88 	n = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0);
    89 	if (len < 0 || !s[len])
    90 		n--;
    91 
    92 	if (wstr->allocated <= wstr->len + n) {
    93 		allocated = (wstr->len + n + 1) * 2;
    94 		str = realloc(wstr->str, allocated * sizeof(wchar_t));
    95 		if (!str)
    96 			return -1;
    97 		wstr->allocated = allocated;
    98 		wstr->str = str;
    99 	}
   100 
   101 	(void)MultiByteToWideChar(CP_UTF8, 0, s, len, wstr->str + wstr->len, n);
   102 	wstr->len += n;
   103 	wstr->str[wstr->len] = 0;
   104 
   105 	return 0;
   106 }
   107 
   108 static void
   109 razor_wstr_destroy(struct razor_wstr *wstr)
   110 {
   111 	free(wstr->str);
   112 	free(wstr);
   113 }
   114 
   115 RAZOR_EXPORT struct razor_atomic *
   116 razor_atomic_open(const char *description)
   117 {
   118 	wchar_t *buf;
   119 	struct razor_atomic *atomic;
   120 
   121 	atomic = zalloc(sizeof *atomic);
   122 	buf = razor_utf8_to_utf16(description, -1);
   123 	atomic->transaction = CreateTransaction(NULL, 0,
   124 						TRANSACTION_DO_NOT_PROMOTE,
   125 						0, 0, 0, buf);
   126 	free(buf);
   127 
   128 	return atomic;
   129 }
   130 
   131 RAZOR_EXPORT int
   132 razor_atomic_commit(struct razor_atomic *atomic)
   133 {
   134 	int retval;
   135 
   136 	if (razor_atomic_in_error_state(atomic))
   137 		return -1;
   138 
   139 	retval = !CommitTransaction(atomic->transaction);
   140 
   141 	if (retval) {
   142 		razor_set_error_mswin(&atomic->error, NULL, GetLastError());
   143 		RollbackTransaction(atomic->transaction);
   144 	}
   145 
   146 	CloseHandle(atomic->transaction);
   147 	atomic->transaction = INVALID_HANDLE_VALUE;
   148 
   149 	return retval;
   150 }
   151 
   152 RAZOR_EXPORT void
   153 razor_atomic_destroy(struct razor_atomic *atomic)
   154 {
   155 	int i;
   156 
   157 	for(i = 0; i < atomic->n_files; i++) {
   158 		if (atomic->files[i].h != INVALID_HANDLE_VALUE) {
   159 			CloseHandle(atomic->files[i].h);
   160 			free(atomic->files[i].path);
   161 		}
   162 	}
   163 	free(atomic->files);
   164 	if (atomic->transaction != INVALID_HANDLE_VALUE) {
   165 		RollbackTransaction(atomic->transaction);
   166 		CloseHandle(atomic->transaction);
   167 	}
   168 	if (atomic->error)
   169 		razor_error_free(atomic->error);
   170 	free(atomic);
   171 }
   172 
   173 RAZOR_EXPORT int
   174 razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root,
   175 		       const char *path)
   176 {
   177 	struct razor_wstr *buffer;
   178 	const char *slash, *next;
   179 	WIN32_FILE_ATTRIBUTE_DATA fa;
   180 	DWORD err;
   181 	int r, creating = 0;
   182 
   183 	if (razor_atomic_in_error_state(atomic))
   184 		return -1;
   185 
   186 	buffer = razor_wstr_create(root, -1);
   187 	slash = buffer->len ? SKIP_DRIVE_LETTER(path) : path;
   188 
   189 	for (; *slash != '\0'; slash = next) {
   190 		next = strpbrk(slash + 1, "/\\");
   191 		if (next == NULL)
   192 			break;
   193 
   194 		razor_wstr_append(buffer, slash, next - slash);
   195 
   196 		if (!creating) {
   197 			if (razor_valid_root_name2(buffer->str))
   198 				continue;
   199 
   200 			r = GetFileAttributesTransactedW(buffer->str,
   201 							 GetFileExInfoStandard,
   202 							 &fa,
   203 							 atomic->transaction);
   204 
   205 			if (!r) {
   206 				err = GetLastError();
   207 				if (err == ERROR_FILE_NOT_FOUND) {
   208 					creating = 1;
   209 				} else {
   210 					razor_set_error_mswin(&atomic->error,
   211 							      buffer->str, err);
   212 					razor_wstr_destroy(buffer);
   213 					return -1;
   214 				}
   215 			} else if (!(fa.dwFileAttributes&
   216 				     FILE_ATTRIBUTE_DIRECTORY)) {
   217 				razor_set_error2(&atomic->error,
   218 						 RAZOR_MSWIN_ERROR,
   219 						 ERROR_DIRECTORY, buffer->str,
   220 						 "Not a directory");
   221 				razor_wstr_destroy(buffer);
   222 				return -1;
   223 			}
   224 		}
   225 		if (creating) {
   226 			if (!CreateDirectoryTransactedW(NULL, buffer->str, NULL,
   227 							atomic->transaction)) {
   228 				razor_set_error_mswin(&atomic->error,
   229 						      buffer->str,
   230 						      GetLastError());
   231 				razor_wstr_destroy(buffer);
   232 				return -1;
   233 			}
   234 
   235 			/* FIXME: What to do about permissions for dirs we
   236 			 * have to create but are not in the cpio archive? */
   237 		}
   238 	}
   239 
   240 	razor_wstr_destroy(buffer);
   241 
   242 	return 0;
   243 }
   244 
   245 RAZOR_EXPORT int
   246 razor_atomic_remove(struct razor_atomic *atomic, const char *path)
   247 {
   248 	wchar_t *buf;
   249 	DWORD err;
   250 
   251 	if (razor_atomic_in_error_state(atomic))
   252 		return -1;
   253 
   254 	buf = razor_utf8_to_utf16(path, -1);
   255 
   256 	if (DeleteFileTransactedW(buf, atomic->transaction)) {
   257 		free(buf);
   258 		return 0;
   259 	}
   260 
   261 	err = GetLastError();
   262 	if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
   263 		free(buf);
   264 		return 0;
   265 	}
   266 
   267 	if (SetFileAttributesTransactedW(buf, FILE_ATTRIBUTE_NORMAL,
   268 					 atomic->transaction)) {
   269 		if (DeleteFileTransactedW(buf, atomic->transaction)) {
   270 			free(buf);
   271 			return 0;
   272 		}
   273 		err = GetLastError();
   274 	}
   275 
   276 	if (RemoveDirectoryTransactedW(buf, atomic->transaction) ||
   277 	    GetLastError() == ERROR_DIR_NOT_EMPTY) {
   278 		free(buf);
   279 		return 0;
   280 	}
   281 
   282 	/*
   283 	 * It would be tempting to use:
   284 	 * 	MoveFileEx(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)
   285 	 * but unless we can guarantee that the system will be rebooted
   286 	 * before we (or some other application) write another file with the
   287 	 * same path, this is likely to cause more problems than it solves.
   288 	 */
   289 
   290 	razor_set_error_mswin(&atomic->error, buf, err);
   291 	free(buf);
   292 	return -1;
   293 }
   294 
   295 RAZOR_EXPORT int
   296 razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath,
   297 			 const char *newpath)
   298 {
   299 	wchar_t *oldbuf, *newbuf;
   300 	const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING;
   301 
   302 	if (razor_atomic_in_error_state(atomic))
   303 		return -1;
   304 
   305 	newbuf = razor_utf8_to_utf16(newpath, -1);
   306 	oldbuf = razor_utf8_to_utf16(oldpath, -1);
   307 
   308 	/*
   309 	 * Passing MOVEFILE_REPLACE_EXISTING to MoveFileTransaction() will
   310 	 * cover every case we care about _except_ replacing an empty
   311 	 * directory with a file. Calling RemoveDirectoryTransacted() will deal
   312 	 * with this case while having no effect in all other cases.
   313 	 */
   314 	(void)RemoveDirectoryTransactedW(newbuf, atomic->transaction);
   315 
   316 	if (!MoveFileTransactedW(oldbuf, newbuf, NULL, NULL, flags,
   317 			         atomic->transaction))
   318 		razor_set_error_mswin(&atomic->error, newbuf, GetLastError());
   319 
   320 	free(newbuf);
   321 	free(oldbuf);
   322 
   323 	return razor_atomic_in_error_state(atomic);
   324 }
   325 
   326 RAZOR_EXPORT int
   327 razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname,
   328 			mode_t mode)
   329 {
   330 	wchar_t *buf;
   331 	DWORD err;
   332 	WIN32_FILE_ATTRIBUTE_DATA fa;
   333 
   334 	if (razor_atomic_in_error_state(atomic))
   335 		return -1;
   336 
   337 	buf = razor_utf8_to_utf16(dirname, -1);
   338 
   339 	if (!CreateDirectoryTransactedW(NULL, buf, NULL, atomic->transaction)) {
   340 		err = GetLastError();
   341 		if (err != ERROR_FILE_EXISTS && err != ERROR_ALREADY_EXISTS) {
   342 abort:
   343 			razor_set_error_mswin(&atomic->error, buf, err);
   344 			free(buf);
   345 			return -1;
   346 		}
   347 
   348 		if (!GetFileAttributesTransactedW(buf, GetFileExInfoStandard,
   349 						  &fa, atomic->transaction))
   350 			goto abort;
   351 
   352 		if (!(fa.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) {
   353 			if (razor_atomic_remove(atomic, dirname)) {
   354 				free(buf);
   355 				return -1;
   356 			}
   357 			if (!CreateDirectoryTransactedW(NULL, buf, NULL,
   358 							atomic->transaction)) {
   359 				err = GetLastError();
   360 				goto abort;
   361 			}
   362 		}
   363 	}
   364 
   365 	free(buf);
   366 
   367 	return 0;
   368 }
   369 
   370 RAZOR_EXPORT int
   371 razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target,
   372 			    const char *path)
   373 {
   374 	if (razor_atomic_in_error_state(atomic))
   375 		return -1;
   376 
   377 	/*
   378 	 * This isn't true, but symbolic links under Windows 7
   379 	 * need to know whether the target is a directory or not
   380 	 * and we don't always know that at the time when the
   381 	 * link is created, so it's a convienent lie for now.
   382 	 */
   383 	razor_set_error(&atomic->error, RAZOR_MSWIN_ERROR, ERROR_NOT_SUPPORTED,
   384 			NULL, "Symbolic links not supported on this platform");
   385 
   386 	return -1;
   387 }
   388 
   389 RAZOR_EXPORT int
   390 razor_atomic_create_file(struct razor_atomic *atomic, const char *filename,
   391 			 mode_t mode)
   392 {
   393 	DWORD attribs;
   394 	struct razor_atomic_file *files;
   395 	int i = atomic->n_files;
   396 
   397 	if (razor_atomic_in_error_state(atomic))
   398 		return -1;
   399 
   400 	files = realloc(atomic->files,
   401 			(atomic->n_files+1) * sizeof(struct razor_atomic_file));
   402 	if (!files) {
   403 		razor_set_error(&atomic->error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
   404 				"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 */